feat: add recipe translation tool with AI translation and settings management
- Add Word document parsing and translation export - Add database configuration management - Add AI/LLM translation configuration - Add translation vocabulary management - Add export settings (colors, page orientation, headers) - Separate database and AI configurations - Add debounced search to prevent database overload
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
import OpenAI from "openai";
|
||||
|
||||
// AI 翻译配置
|
||||
interface AIConfig {
|
||||
apiKey: string;
|
||||
baseUrl?: string;
|
||||
model?: string;
|
||||
}
|
||||
|
||||
// 默认配置(从环境变量)
|
||||
function getDefaultConfig(): AIConfig {
|
||||
return {
|
||||
apiKey: process.env.AI_API_KEY || "",
|
||||
baseUrl: process.env.AI_BASE_URL || undefined,
|
||||
model: process.env.AI_MODEL || "gpt-3.5-turbo",
|
||||
};
|
||||
}
|
||||
|
||||
// 模块级配置存储
|
||||
let savedConfig: AIConfig | null = null;
|
||||
|
||||
// 设置保存的配置
|
||||
export function setAIConfig(config: AIConfig) {
|
||||
savedConfig = config;
|
||||
}
|
||||
|
||||
// 获取配置(优先使用保存的配置)
|
||||
export function getAIConfig(): AIConfig {
|
||||
return savedConfig || getDefaultConfig();
|
||||
}
|
||||
|
||||
// 解析翻译结果
|
||||
function parseTranslation(content: string): string | null {
|
||||
const patterns = [
|
||||
/english\s*:\s*['"]([^'"]+)['"]/i,
|
||||
/english\s*[:\s]+([^,'"\n]+)/i,
|
||||
/['"]([^'"]+)['"]\s*[,}].*english/i,
|
||||
/^([^,]+)\s*,\s*english\s*:\s*['"]([^'"]+)['"]/i,
|
||||
];
|
||||
|
||||
for (const pattern of patterns) {
|
||||
const match = content.match(pattern);
|
||||
if (match) {
|
||||
return match[1]?.trim() || null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// 翻译单个词组(带重试)
|
||||
export async function translateWithAI(
|
||||
word: string,
|
||||
config?: AIConfig,
|
||||
maxRetries: number = 3
|
||||
): Promise<string | null> {
|
||||
const aiConfig = config || getAIConfig();
|
||||
|
||||
if (!aiConfig.apiKey) {
|
||||
throw new Error("AI API Key 未配置");
|
||||
}
|
||||
|
||||
const client = new OpenAI({
|
||||
apiKey: aiConfig.apiKey,
|
||||
baseURL: aiConfig.baseUrl,
|
||||
});
|
||||
|
||||
const prompt = `你是一个专业的中文到英文翻译助手。
|
||||
|
||||
任务:将给定的中文词组翻译成英文。
|
||||
|
||||
严格要求:
|
||||
1. 必须严格返回 JSON 格式:{"word":"中文原文","english":"英文翻译"}
|
||||
2. 不要返回任何其他内容,不要解释
|
||||
3. 只返回这一行 JSON
|
||||
4. 翻译要简洁、准确
|
||||
|
||||
中文词组:${word}
|
||||
|
||||
请立即返回 JSON 结果:`;
|
||||
|
||||
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
||||
try {
|
||||
const completion = await client.chat.completions.create({
|
||||
model: aiConfig.model || "gpt-3.5-turbo",
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content: "你是一个严格遵循指令的翻译助手。必须只返回要求的 JSON 格式,不要返回任何其他内容。",
|
||||
},
|
||||
{ role: "user", content: prompt },
|
||||
],
|
||||
temperature: 0,
|
||||
max_tokens: 50,
|
||||
});
|
||||
|
||||
const content = completion.choices[0]?.message?.content || "";
|
||||
const translation = parseTranslation(content);
|
||||
|
||||
if (translation && translation.length > 0) {
|
||||
return translation;
|
||||
}
|
||||
|
||||
// Fallback: 提取英文单词
|
||||
const fallbackMatch = content.match(/[a-zA-Z][a-zA-Z\s-]*/);
|
||||
if (fallbackMatch) {
|
||||
const fallback = fallbackMatch[0].trim();
|
||||
if (fallback.length > 1) {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Attempt ${attempt + 1} error:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// 批量翻译词组
|
||||
export async function batchTranslateWithAI(
|
||||
words: string[],
|
||||
config?: AIConfig
|
||||
): Promise<Map<string, string>> {
|
||||
const results = new Map<string, string>();
|
||||
|
||||
for (const word of words) {
|
||||
try {
|
||||
const translation = await translateWithAI(word, config);
|
||||
if (translation) {
|
||||
results.set(word, translation);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to translate "${word}":`, error);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
Reference in New Issue
Block a user