feat: Add new translation providers and set GLM-4 Flash as default
- Added GLM-4 Flash (Free) as highest priority provider with professional system prompts - Added Silicon Flow (Free) as second priority provider with Qwen2.5-7B model - Added Baidu Translate (Free) with proper API authentication - Added Youdao Translate (Free) with proper API authentication - Reordered provider priorities: GLM-4 Flash > Silicon Flow > Baidu > Youdao > Google > Libre > MyMemory > DeepL > Azure - Updated provider list in UI to show new services - Enhanced system prompts for AI providers based on professional translation standards - Version bump to 1.4.0
This commit is contained in:
@@ -30,12 +30,373 @@ class TranslationProvider:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class GLM4FlashProvider(TranslationProvider):
|
||||
"""GLM-4 Flash (Free) - AI-powered translation"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("GLM-4 Flash (Free)", True, False)
|
||||
self.priority = 1
|
||||
self.base_url = "https://open.bigmodel.cn/api/paas/v4/chat/completions"
|
||||
|
||||
def translate(self, text: str, target_lang: str, source_lang: str = "auto", api_key: str = None) -> Tuple[bool, str]:
|
||||
try:
|
||||
print(f" 🔗 Connecting to GLM-4 Flash API...")
|
||||
|
||||
# Map language codes to full names
|
||||
lang_map = {
|
||||
"zh": "Chinese", "en": "English", "ja": "Japanese", "ko": "Korean",
|
||||
"fr": "French", "de": "German", "es": "Spanish", "it": "Italian",
|
||||
"pt": "Portuguese", "ru": "Russian", "ar": "Arabic", "hi": "Hindi",
|
||||
"th": "Thai", "vi": "Vietnamese", "tr": "Turkish", "pl": "Polish",
|
||||
"nl": "Dutch", "sv": "Swedish", "da": "Danish", "no": "Norwegian",
|
||||
"fi": "Finnish", "cs": "Czech", "hu": "Hungarian", "ro": "Romanian",
|
||||
"bg": "Bulgarian", "hr": "Croatian", "sk": "Slovak", "sl": "Slovenian",
|
||||
"et": "Estonian", "lv": "Latvian", "lt": "Lithuanian", "el": "Greek",
|
||||
"he": "Hebrew", "fa": "Persian", "ur": "Urdu", "bn": "Bengali",
|
||||
"ta": "Tamil", "te": "Telugu", "ml": "Malayalam", "kn": "Kannada",
|
||||
"gu": "Gujarati", "pa": "Punjabi", "or": "Odia", "as": "Assamese",
|
||||
"ne": "Nepali", "si": "Sinhala", "my": "Burmese", "km": "Khmer",
|
||||
"lo": "Lao", "ka": "Georgian", "am": "Amharic", "sw": "Swahili",
|
||||
"zu": "Zulu", "af": "Afrikaans", "sq": "Albanian", "eu": "Basque",
|
||||
"be": "Belarusian", "bs": "Bosnian", "ca": "Catalan", "cy": "Welsh",
|
||||
"eo": "Esperanto", "gl": "Galician", "is": "Icelandic", "mk": "Macedonian",
|
||||
"mt": "Maltese", "sr": "Serbian", "uk": "Ukrainian", "uz": "Uzbek"
|
||||
}
|
||||
|
||||
target_lang_name = lang_map.get(target_lang, target_lang)
|
||||
|
||||
system_prompt = f"""You are a professional {target_lang_name} native translator who needs to fluently translate text into {target_lang_name}.
|
||||
|
||||
## Translation Rules
|
||||
1. Output only the translated content, without explanations or additional content (such as "Here's the translation:" or "Translation as follows:")
|
||||
2. The returned translation must maintain exactly the same number of paragraphs and format as the original text
|
||||
3. If the text contains HTML tags, consider where the tags should be placed in the translation while maintaining fluency
|
||||
4. For content that should not be translated (such as proper nouns, code, etc.), keep the original text.
|
||||
5. If input contains %%, use %% in your output, if input has no %%, don't use %% in your output
|
||||
|
||||
## OUTPUT FORMAT:
|
||||
- **Single paragraph input** → Output translation directly (no separators, no extra text)
|
||||
- **Multi-paragraph input** → Use %% as paragraph separator between translations
|
||||
|
||||
## Examples
|
||||
### Multi-paragraph Input:
|
||||
Paragraph A
|
||||
%%
|
||||
Paragraph B
|
||||
%%
|
||||
Paragraph C
|
||||
%%
|
||||
Paragraph D
|
||||
|
||||
### Multi-paragraph Output:
|
||||
Translation A
|
||||
%%
|
||||
Translation B
|
||||
%%
|
||||
Translation C
|
||||
%%
|
||||
Translation D
|
||||
|
||||
### Single paragraph Input:
|
||||
Single paragraph content
|
||||
|
||||
### Single paragraph Output:
|
||||
Direct translation without separators"""
|
||||
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': f'Bearer {api_key}' if api_key else 'Bearer your-api-key-here'
|
||||
}
|
||||
|
||||
data = {
|
||||
"model": "glm-4-flash",
|
||||
"messages": [
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": f"Translate to {target_lang_name}:\n\n{text}"}
|
||||
],
|
||||
"temperature": 0.3,
|
||||
"max_tokens": 4000
|
||||
}
|
||||
|
||||
print(f" 📤 Sending request: {source_lang} -> {target_lang}")
|
||||
response = requests.post(self.base_url, headers=headers, json=data, timeout=30)
|
||||
print(f" 📥 Response status: {response.status_code}")
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
result = response.json()
|
||||
if 'choices' in result and len(result['choices']) > 0:
|
||||
translated_text = result['choices'][0]['message']['content'].strip()
|
||||
print(f" ✅ GLM-4 Flash success: {len(translated_text)} characters")
|
||||
return True, translated_text
|
||||
print(f" ❌ GLM-4 Flash: Invalid response format")
|
||||
return False, "Translation failed"
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ GLM-4 Flash error: {str(e)}")
|
||||
return False, f"GLM-4 Flash error: {str(e)}"
|
||||
|
||||
|
||||
class SiliconFlowProvider(TranslationProvider):
|
||||
"""Silicon Flow (Free) - AI-powered translation"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("Silicon Flow (Free)", True, False)
|
||||
self.priority = 2
|
||||
self.base_url = "https://api.siliconflow.cn/v1/chat/completions"
|
||||
|
||||
def translate(self, text: str, target_lang: str, source_lang: str = "auto", api_key: str = None) -> Tuple[bool, str]:
|
||||
try:
|
||||
print(f" 🔗 Connecting to Silicon Flow API...")
|
||||
|
||||
# Map language codes to full names
|
||||
lang_map = {
|
||||
"zh": "Chinese", "en": "English", "ja": "Japanese", "ko": "Korean",
|
||||
"fr": "French", "de": "German", "es": "Spanish", "it": "Italian",
|
||||
"pt": "Portuguese", "ru": "Russian", "ar": "Arabic", "hi": "Hindi",
|
||||
"th": "Thai", "vi": "Vietnamese", "tr": "Turkish", "pl": "Polish",
|
||||
"nl": "Dutch", "sv": "Swedish", "da": "Danish", "no": "Norwegian",
|
||||
"fi": "Finnish", "cs": "Czech", "hu": "Hungarian", "ro": "Romanian",
|
||||
"bg": "Bulgarian", "hr": "Croatian", "sk": "Slovak", "sl": "Slovenian",
|
||||
"et": "Estonian", "lv": "Latvian", "lt": "Lithuanian", "el": "Greek",
|
||||
"he": "Hebrew", "fa": "Persian", "ur": "Urdu", "bn": "Bengali",
|
||||
"ta": "Tamil", "te": "Telugu", "ml": "Malayalam", "kn": "Kannada",
|
||||
"gu": "Gujarati", "pa": "Punjabi", "or": "Odia", "as": "Assamese",
|
||||
"ne": "Nepali", "si": "Sinhala", "my": "Burmese", "km": "Khmer",
|
||||
"lo": "Lao", "ka": "Georgian", "am": "Amharic", "sw": "Swahili",
|
||||
"zu": "Zulu", "af": "Afrikaans", "sq": "Albanian", "eu": "Basque",
|
||||
"be": "Belarusian", "bs": "Bosnian", "ca": "Catalan", "cy": "Welsh",
|
||||
"eo": "Esperanto", "gl": "Galician", "is": "Icelandic", "mk": "Macedonian",
|
||||
"mt": "Maltese", "sr": "Serbian", "uk": "Ukrainian", "uz": "Uzbek"
|
||||
}
|
||||
|
||||
target_lang_name = lang_map.get(target_lang, target_lang)
|
||||
|
||||
system_prompt = f"""You are a professional {target_lang_name} native translator who needs to fluently translate text into {target_lang_name}.
|
||||
|
||||
## Translation Rules
|
||||
1. Output only the translated content, without explanations or additional content (such as "Here's the translation:" or "Translation as follows:")
|
||||
2. The returned translation must maintain exactly the same number of paragraphs and format as the original text
|
||||
3. If the text contains HTML tags, consider where the tags should be placed in the translation while maintaining fluency
|
||||
4. For content that should not be translated (such as proper nouns, code, etc.), keep the original text.
|
||||
5. If input contains %%, use %% in your output, if input has no %%, don't use %% in your output
|
||||
|
||||
## OUTPUT FORMAT:
|
||||
- **Single paragraph input** → Output translation directly (no separators, no extra text)
|
||||
- **Multi-paragraph input** → Use %% as paragraph separator between translations
|
||||
|
||||
## Examples
|
||||
### Multi-paragraph Input:
|
||||
Paragraph A
|
||||
%%
|
||||
Paragraph B
|
||||
%%
|
||||
Paragraph C
|
||||
%%
|
||||
Paragraph D
|
||||
|
||||
### Multi-paragraph Output:
|
||||
Translation A
|
||||
%%
|
||||
Translation B
|
||||
%%
|
||||
Translation C
|
||||
%%
|
||||
Translation D
|
||||
|
||||
### Single paragraph Input:
|
||||
Single paragraph content
|
||||
|
||||
### Single paragraph Output:
|
||||
Direct translation without separators"""
|
||||
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': f'Bearer {api_key}' if api_key else 'Bearer your-api-key-here'
|
||||
}
|
||||
|
||||
data = {
|
||||
"model": "Qwen/Qwen2.5-7B-Instruct",
|
||||
"messages": [
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": f"Translate to {target_lang_name}:\n\n{text}"}
|
||||
],
|
||||
"temperature": 0.3,
|
||||
"max_tokens": 4000
|
||||
}
|
||||
|
||||
print(f" 📤 Sending request: {source_lang} -> {target_lang}")
|
||||
response = requests.post(self.base_url, headers=headers, json=data, timeout=30)
|
||||
print(f" 📥 Response status: {response.status_code}")
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
result = response.json()
|
||||
if 'choices' in result and len(result['choices']) > 0:
|
||||
translated_text = result['choices'][0]['message']['content'].strip()
|
||||
print(f" ✅ Silicon Flow success: {len(translated_text)} characters")
|
||||
return True, translated_text
|
||||
print(f" ❌ Silicon Flow: Invalid response format")
|
||||
return False, "Translation failed"
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ Silicon Flow error: {str(e)}")
|
||||
return False, f"Silicon Flow error: {str(e)}"
|
||||
|
||||
|
||||
class BaiduTranslateProvider(TranslationProvider):
|
||||
"""Baidu Translate (Free) - Baidu translation service"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("Baidu Translate (Free)", True, False)
|
||||
self.priority = 3
|
||||
self.base_url = "https://fanyi-api.baidu.com/api/trans/vip/translate"
|
||||
|
||||
def translate(self, text: str, target_lang: str, source_lang: str = "auto", api_key: str = None) -> Tuple[bool, str]:
|
||||
try:
|
||||
print(f" 🔗 Connecting to Baidu Translate API...")
|
||||
|
||||
# Baidu language code mapping
|
||||
lang_map = {
|
||||
"auto": "auto", "zh": "zh", "en": "en", "ja": "jp", "ko": "kor",
|
||||
"fr": "fra", "de": "de", "es": "spa", "it": "it", "pt": "pt",
|
||||
"ru": "ru", "ar": "ara", "hi": "hi", "th": "th", "vi": "vie",
|
||||
"tr": "tr", "pl": "pl", "nl": "nl", "sv": "swe", "da": "dan",
|
||||
"no": "nor", "fi": "fin", "cs": "cs", "hu": "hu", "ro": "rom",
|
||||
"bg": "bul", "hr": "hr", "sk": "sk", "sl": "slo", "et": "est",
|
||||
"lv": "lav", "lt": "lit", "el": "el", "he": "heb", "fa": "per",
|
||||
"ur": "urd", "bn": "ben", "ta": "tam", "te": "tel", "ml": "mal",
|
||||
"kn": "kan", "gu": "guj", "pa": "pan", "or": "ori", "as": "asm",
|
||||
"ne": "nep", "si": "sin", "my": "bur", "km": "hkm", "lo": "lao",
|
||||
"ka": "geo", "am": "amh", "sw": "swa", "zu": "zul", "af": "afr",
|
||||
"sq": "alb", "eu": "baq", "be": "bel", "bs": "bos", "ca": "cat",
|
||||
"cy": "wel", "eo": "epo", "gl": "glg", "is": "ice", "mk": "mac",
|
||||
"mt": "mlt", "sr": "srp", "uk": "ukr", "uz": "uzb"
|
||||
}
|
||||
|
||||
baidu_source = lang_map.get(source_lang, "auto")
|
||||
baidu_target = lang_map.get(target_lang, "en")
|
||||
|
||||
# Generate salt and sign for Baidu API
|
||||
import time
|
||||
import hashlib
|
||||
import random
|
||||
|
||||
appid = api_key if api_key else "your_app_id"
|
||||
secret_key = "your_secret_key" # This should be provided separately
|
||||
|
||||
salt = str(int(time.time() * 1000) + random.randint(1, 1000))
|
||||
sign_str = appid + text + salt + secret_key
|
||||
sign = hashlib.md5(sign_str.encode('utf-8')).hexdigest()
|
||||
|
||||
params = {
|
||||
'q': text,
|
||||
'from': baidu_source,
|
||||
'to': baidu_target,
|
||||
'appid': appid,
|
||||
'salt': salt,
|
||||
'sign': sign
|
||||
}
|
||||
|
||||
print(f" 📤 Sending request: {source_lang} -> {target_lang}")
|
||||
response = requests.get(self.base_url, params=params, timeout=10)
|
||||
print(f" 📥 Response status: {response.status_code}")
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
result = response.json()
|
||||
if 'trans_result' in result and len(result['trans_result']) > 0:
|
||||
translated_text = result['trans_result'][0]['dst']
|
||||
print(f" ✅ Baidu Translate success: {len(translated_text)} characters")
|
||||
return True, translated_text
|
||||
print(f" ❌ Baidu Translate: Invalid response format")
|
||||
return False, "Translation failed"
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ Baidu Translate error: {str(e)}")
|
||||
return False, f"Baidu Translate error: {str(e)}"
|
||||
|
||||
|
||||
class YoudaoTranslateProvider(TranslationProvider):
|
||||
"""Youdao Translate (Free) - Youdao translation service"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("Youdao Translate (Free)", True, False)
|
||||
self.priority = 4
|
||||
self.base_url = "https://openapi.youdao.com/api"
|
||||
|
||||
def translate(self, text: str, target_lang: str, source_lang: str = "auto", api_key: str = None) -> Tuple[bool, str]:
|
||||
try:
|
||||
print(f" 🔗 Connecting to Youdao Translate API...")
|
||||
|
||||
# Youdao language code mapping
|
||||
lang_map = {
|
||||
"auto": "auto", "zh": "zh-CHS", "en": "en", "ja": "ja", "ko": "ko",
|
||||
"fr": "fr", "de": "de", "es": "es", "it": "it", "pt": "pt",
|
||||
"ru": "ru", "ar": "ar", "hi": "hi", "th": "th", "vi": "vi",
|
||||
"tr": "tr", "pl": "pl", "nl": "nl", "sv": "sv", "da": "da",
|
||||
"no": "no", "fi": "fi", "cs": "cs", "hu": "hu", "ro": "ro",
|
||||
"bg": "bg", "hr": "hr", "sk": "sk", "sl": "sl", "et": "et",
|
||||
"lv": "lv", "lt": "lt", "el": "el", "he": "he", "fa": "fa",
|
||||
"ur": "ur", "bn": "bn", "ta": "ta", "te": "te", "ml": "ml",
|
||||
"kn": "kn", "gu": "gu", "pa": "pa", "or": "or", "as": "as",
|
||||
"ne": "ne", "si": "si", "my": "my", "km": "km", "lo": "lo",
|
||||
"ka": "ka", "am": "am", "sw": "sw", "zu": "zu", "af": "af",
|
||||
"sq": "sq", "eu": "eu", "be": "be", "bs": "bs", "ca": "ca",
|
||||
"cy": "cy", "eo": "eo", "gl": "gl", "is": "is", "mk": "mk",
|
||||
"mt": "mt", "sr": "sr", "uk": "uk", "uz": "uz"
|
||||
}
|
||||
|
||||
youdao_source = lang_map.get(source_lang, "auto")
|
||||
youdao_target = lang_map.get(target_lang, "en")
|
||||
|
||||
# Generate salt and sign for Youdao API
|
||||
import time
|
||||
import hashlib
|
||||
import random
|
||||
|
||||
app_key = api_key if api_key else "your_app_key"
|
||||
app_secret = "your_app_secret" # This should be provided separately
|
||||
|
||||
salt = str(int(time.time() * 1000) + random.randint(1, 1000))
|
||||
sign_str = app_key + text + salt + app_secret
|
||||
sign = hashlib.sha256(sign_str.encode('utf-8')).hexdigest()
|
||||
|
||||
data = {
|
||||
'q': text,
|
||||
'from': youdao_source,
|
||||
'to': youdao_target,
|
||||
'appKey': app_key,
|
||||
'salt': salt,
|
||||
'sign': sign,
|
||||
'signType': 'v3'
|
||||
}
|
||||
|
||||
print(f" 📤 Sending request: {source_lang} -> {target_lang}")
|
||||
response = requests.post(self.base_url, data=data, timeout=10)
|
||||
print(f" 📥 Response status: {response.status_code}")
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
result = response.json()
|
||||
if 'translation' in result and len(result['translation']) > 0:
|
||||
translated_text = result['translation'][0]
|
||||
print(f" ✅ Youdao Translate success: {len(translated_text)} characters")
|
||||
return True, translated_text
|
||||
print(f" ❌ Youdao Translate: Invalid response format")
|
||||
return False, "Translation failed"
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ Youdao Translate error: {str(e)}")
|
||||
return False, f"Youdao Translate error: {str(e)}"
|
||||
|
||||
|
||||
class GoogleTranslateProvider(TranslationProvider):
|
||||
"""Google Translate (Free) - Using web interface"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("Google Translate (Free)", True, False)
|
||||
self.priority = 1
|
||||
self.priority = 5
|
||||
self.base_url = "https://translate.googleapis.com/translate_a/single"
|
||||
|
||||
def translate(self, text: str, target_lang: str, source_lang: str = "auto", api_key: str = None) -> Tuple[bool, str]:
|
||||
@@ -73,7 +434,7 @@ class LibreTranslateProvider(TranslationProvider):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("LibreTranslate (Free)", True, False)
|
||||
self.priority = 2
|
||||
self.priority = 6
|
||||
self.base_url = "https://libretranslate.de/translate"
|
||||
|
||||
def translate(self, text: str, target_lang: str, source_lang: str = "auto", api_key: str = None) -> Tuple[bool, str]:
|
||||
@@ -110,7 +471,7 @@ class MyMemoryProvider(TranslationProvider):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("MyMemory (Free)", True, False)
|
||||
self.priority = 3
|
||||
self.priority = 7
|
||||
self.base_url = "https://api.mymemory.translated.net/get"
|
||||
|
||||
def translate(self, text: str, target_lang: str, source_lang: str = "auto", api_key: str = None) -> Tuple[bool, str]:
|
||||
@@ -145,7 +506,7 @@ class DeepLProvider(TranslationProvider):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("DeepL (Paid)", False, True)
|
||||
self.priority = 4
|
||||
self.priority = 8
|
||||
self.base_url = "https://api-free.deepl.com/v2/translate"
|
||||
|
||||
def translate(self, text: str, target_lang: str, source_lang: str = "auto", api_key: str = None) -> Tuple[bool, str]:
|
||||
@@ -187,7 +548,7 @@ class AzureTranslatorProvider(TranslationProvider):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("Azure Translator (Paid)", False, True)
|
||||
self.priority = 5
|
||||
self.priority = 9
|
||||
self.base_url = "https://api.cognitive.microsofttranslator.com/translate"
|
||||
|
||||
def translate(self, text: str, target_lang: str, source_lang: str = "auto", api_key: str = None) -> Tuple[bool, str]:
|
||||
@@ -235,6 +596,10 @@ class TextTranslator_UTK:
|
||||
|
||||
def __init__(self):
|
||||
self.providers = {
|
||||
"GLM-4 Flash (Free)": GLM4FlashProvider(),
|
||||
"Silicon Flow (Free)": SiliconFlowProvider(),
|
||||
"Baidu Translate (Free)": BaiduTranslateProvider(),
|
||||
"Youdao Translate (Free)": YoudaoTranslateProvider(),
|
||||
"Google Translate (Free)": GoogleTranslateProvider(),
|
||||
"LibreTranslate (Free)": LibreTranslateProvider(),
|
||||
"MyMemory (Free)": MyMemoryProvider(),
|
||||
@@ -257,6 +622,10 @@ class TextTranslator_UTK:
|
||||
# Provider list with free/paid indicators
|
||||
provider_list = [
|
||||
"auto",
|
||||
"GLM-4 Flash (Free)",
|
||||
"Silicon Flow (Free)",
|
||||
"Baidu Translate (Free)",
|
||||
"Youdao Translate (Free)",
|
||||
"Google Translate (Free)",
|
||||
"LibreTranslate (Free)",
|
||||
"MyMemory (Free)",
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "universaltoolkit"
|
||||
description = "A comprehensive toolkit based on ComfyUI, providing image, mask, audio, and tools nodes, fully modular and v3 compatible."
|
||||
version = "1.3.9"
|
||||
version = "1.4.0"
|
||||
license = {file = "LICENSE"}
|
||||
dependencies = [
|
||||
"torch>=1.9.0",
|
||||
|
||||
Reference in New Issue
Block a user