205 lines
7.0 KiB
Python
205 lines
7.0 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
快速翻译脚本
|
|
用于快速翻译 ak_contents 表内容
|
|
"""
|
|
|
|
import asyncio
|
|
import aiohttp
|
|
import json
|
|
import logging
|
|
|
|
# 配置日志
|
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# ========== 配置区域 - 请修改以下参数 ==========
|
|
SUPABASE_URL = "YOUR_SUPABASE_URL" # 例如: https://abcdefgh.supabase.co
|
|
SUPABASE_KEY = "YOUR_SUPABASE_KEY" # 您的 Supabase anon key
|
|
RAGEFLOW_API_KEY = "YOUR_RAGEFLOW_KEY" # 您的 RageFlow API key
|
|
|
|
# 翻译设置
|
|
TARGET_LANGUAGES = ["en", "ja"] # 目标语言
|
|
CONTENT_LIMIT = 5 # 每次翻译的内容数量
|
|
MODEL = "gpt-4" # 使用的模型
|
|
# ===============================================
|
|
|
|
class QuickTranslator:
|
|
def __init__(self):
|
|
self.session = None
|
|
|
|
async def __aenter__(self):
|
|
self.session = aiohttp.ClientSession()
|
|
return self
|
|
|
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
if self.session:
|
|
await self.session.close()
|
|
|
|
async def get_contents(self):
|
|
"""获取待翻译内容"""
|
|
headers = {
|
|
"apikey": SUPABASE_KEY,
|
|
"Authorization": f"Bearer {SUPABASE_KEY}",
|
|
}
|
|
|
|
url = f"{SUPABASE_URL}/rest/v1/ak_contents?select=id,title,content&limit={CONTENT_LIMIT}&order=created_at.desc"
|
|
|
|
async with self.session.get(url, headers=headers) as response:
|
|
if response.status == 200:
|
|
return await response.json()
|
|
else:
|
|
logger.error(f"获取内容失败: {response.status}")
|
|
return []
|
|
|
|
async def translate_text(self, text, target_lang):
|
|
"""翻译文本"""
|
|
if not text.strip():
|
|
return ""
|
|
|
|
lang_map = {"en": "English", "ja": "Japanese", "fr": "French"}
|
|
target_name = lang_map.get(target_lang, target_lang)
|
|
|
|
prompt = f"Please translate the following Chinese text to {target_name}:\n\n{text}\n\nTranslation:"
|
|
|
|
payload = {
|
|
"model": MODEL,
|
|
"messages": [
|
|
{"role": "user", "content": prompt}
|
|
],
|
|
"temperature": 0.3,
|
|
"max_tokens": 2048
|
|
}
|
|
|
|
headers = {
|
|
"Authorization": f"Bearer {RAGEFLOW_API_KEY}",
|
|
"Content-Type": "application/json"
|
|
}
|
|
|
|
try:
|
|
async with self.session.post(
|
|
"https://api.rageflow.ai/v1/chat/completions",
|
|
json=payload,
|
|
headers=headers
|
|
) as response:
|
|
if response.status == 200:
|
|
data = await response.json()
|
|
return data["choices"][0]["message"]["content"].strip()
|
|
else:
|
|
logger.error(f"翻译失败: {response.status}")
|
|
return ""
|
|
except Exception as e:
|
|
logger.error(f"翻译异常: {e}")
|
|
return ""
|
|
|
|
async def save_translation(self, content_id, language, title, content):
|
|
"""保存翻译结果"""
|
|
headers = {
|
|
"apikey": SUPABASE_KEY,
|
|
"Authorization": f"Bearer {SUPABASE_KEY}",
|
|
"Content-Type": "application/json"
|
|
}
|
|
|
|
data = {
|
|
"content_id": content_id,
|
|
"language": language,
|
|
"title": title,
|
|
"content": content,
|
|
"translation_source": "rageflow_auto"
|
|
}
|
|
|
|
# 先检查是否已存在
|
|
check_url = f"{SUPABASE_URL}/rest/v1/ak_content_translations?content_id=eq.{content_id}&language=eq.{language}&select=id"
|
|
async with self.session.get(check_url, headers=headers) as response:
|
|
if response.status == 200:
|
|
existing = await response.json()
|
|
if existing:
|
|
logger.info(f"翻译已存在,跳过: {content_id} -> {language}")
|
|
return True
|
|
|
|
# 插入新翻译
|
|
async with self.session.post(
|
|
f"{SUPABASE_URL}/rest/v1/ak_content_translations",
|
|
json=data,
|
|
headers=headers
|
|
) as response:
|
|
success = response.status in [200, 201]
|
|
if success:
|
|
logger.info(f"翻译保存成功: {content_id} -> {language}")
|
|
else:
|
|
logger.error(f"保存失败: {response.status}")
|
|
return success
|
|
|
|
async def run_translation(self):
|
|
"""执行翻译"""
|
|
logger.info("开始获取内容...")
|
|
contents = await self.get_contents()
|
|
|
|
if not contents:
|
|
logger.warning("没有找到内容")
|
|
return
|
|
|
|
logger.info(f"找到 {len(contents)} 条内容,开始翻译...")
|
|
|
|
total_success = 0
|
|
total_failed = 0
|
|
|
|
for content in contents:
|
|
content_id = content["id"]
|
|
title = content.get("title", "")
|
|
content_text = content.get("content", "")
|
|
|
|
logger.info(f"翻译内容: {title[:30]}...")
|
|
|
|
for target_lang in TARGET_LANGUAGES:
|
|
try:
|
|
# 翻译标题和内容
|
|
translated_title = await self.translate_text(title, target_lang)
|
|
await asyncio.sleep(1) # 避免API限制
|
|
|
|
translated_content = await self.translate_text(content_text, target_lang)
|
|
await asyncio.sleep(1)
|
|
|
|
if translated_title and translated_content:
|
|
# 保存翻译
|
|
success = await self.save_translation(
|
|
content_id, target_lang, translated_title, translated_content
|
|
)
|
|
if success:
|
|
total_success += 1
|
|
else:
|
|
total_failed += 1
|
|
else:
|
|
logger.error(f"翻译失败: {content_id} -> {target_lang}")
|
|
total_failed += 1
|
|
|
|
except Exception as e:
|
|
logger.error(f"处理异常: {content_id} -> {target_lang}: {e}")
|
|
total_failed += 1
|
|
|
|
logger.info(f"翻译完成!成功: {total_success}, 失败: {total_failed}")
|
|
|
|
async def main():
|
|
"""主函数"""
|
|
print("=" * 50)
|
|
print("🌍 简化版自动翻译服务")
|
|
print("=" * 50)
|
|
print(f"📝 目标语言: {', '.join(TARGET_LANGUAGES)}")
|
|
print(f"📊 内容限制: {CONTENT_LIMIT} 条")
|
|
print(f"🤖 使用模型: {MODEL}")
|
|
print("=" * 50)
|
|
|
|
# 检查配置
|
|
if "YOUR_" in SUPABASE_URL or "YOUR_" in SUPABASE_KEY or "YOUR_" in RAGEFLOW_API_KEY:
|
|
print("❌ 请先在脚本顶部配置正确的 API 参数!")
|
|
return
|
|
|
|
async with QuickTranslator() as translator:
|
|
await translator.run_translation()
|
|
|
|
print("✅ 翻译任务完成!")
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|