diff --git a/README.md b/README.md index 0b09383..f6201a5 100644 --- a/README.md +++ b/README.md @@ -1,137 +1,157 @@ # 文本翻译 该自定义节点扩展 适用于 [ComfyUI](https://github.com/comfyanonymous/ComfyUI)。 -可以自动识别输入的语言类型,并将其翻译成英语。 +现版本无需申请翻译API,直接使用即可。 +现已添加的翻译平台:阿里巴巴(Alibaba)、必应(Bing)、谷歌(Google)。 -## 效果如下 - +现已添加的翻译语言:中文(zh)、英文(en)。 -### 在 [StableSwarmUI](https://github.com/Stability-AI/StableSwarmUI) 中使用 ComfyUI 作为后端的效果如下: +更多翻译平台请见 [Translators](https://github.com/UlionTse/translators)。其他的 翻译平台、翻译语言 等 需要自行添加,下文会给出其操作方法。 - +## 注意: +1、更新前 需要将2024年05月19日前使用的老项目删除,当前项目与老项目完全不兼容。 -## 说明 -- 该节点采用了[腾讯翻译API](https://cloud.tencent.com/product/tmt)进行文本翻译。 -- 可能会许多人需要使用这种类型的节点,但我目前尚未找到相关的开放资源,所以把这个分享给大家。 -- 鉴于当前为个人使用的节点,暂时仅支持一个翻译API。如果将来有更多用户使用,可以考虑添加更多API支持。 -- 因为日常并不经常使用Python,且刚刚接触了这个语言和ComfyUI自定义节点,所以可能存在一些未知的BUG。但目前为止,自己使用过程中尚未遇到过BUG。 -- 随着使用这个节点,我将会对它进行修改、新增以及完善。 -- 如果这对你有所帮助,可以给一个小星星呀~ +2、该项目之前的项目名为 `ComfyUI_Text_Translation_zh_CN` 现修改为 `ComfyUI_Text_Translation`。 -## 开始正文 -### 1、节点介绍 -(1)节点有两个文本输入和一个翻译启用开关 +## 1、节点如下: +### 文本翻译 + - +默认使用必应翻译平台,自动识别语言翻译成英文。 -翻译开关:开启(enabled);关闭(disabled) +### 文本翻译 V2 + -(2)开启翻译后,如果在 “需翻译文本” 位置输入的内容不含有汉字,则不会调用 翻译API(避免浪费资源)。 +默认隐藏多余选项,点击 `Show / Hide button` 显示或隐藏更多选项。 -(3)如果想不显示“不需要翻译文本”的输入框,在该节点上点击鼠标右键,把将 text_normal 转换成输入模式即可,并不会影响功能。 +### 文本联接 + -`text_trans 为需要翻译的文本` `text_normal 为无需翻译的文本` + - +自动根据连接的数量增加/减少 输入端,将输入端的值拼接成一个字符串。 +### 文本 + -(4)添加该节点,可以在   utils  中选择,也可以双击鼠标左键搜索 `TencentTextTranslation`。 +文本输入框。 - +### 文本开关 + +文本开关,关闭则输出空字符串。 -(5)如果想要查看翻译后的文本,你可以在打开ComfyUI时弹出来的终端上查看。 +### 获取翻译平台列表 + - +获取翻译平台列表,同时会在终端上显示。 -当然你也可以选择在   .node/tencent_translation.py   文件中,把输出语句前加 “#” 注释掉。 + - +想要在ComfyUI上显示输出文本可以使用,如 [ComfyUI-Custom-Scripts](https://github.com/pythongosssss/ComfyUI-Custom-Scripts) 中的 `ShowText` 或其他节点 -(不显示它,用处不大。建议保留,可用于检查文本翻译是否正常。) +## 2、安装 -如果在有这个输出语句的前提下,终端上没有显示,那么会是这个翻译API或代码有问题,可以向我反馈。当然请求翻译API时有问题也会在终端上显示。 +### (1)添加该节点到 ComfyUI -(6)如果你想修改翻译后的语言类型等功能,可在代码里修改。想要达到   WebUI   中可实时翻译或点击翻译的效果,该节点目前做不到,因为我不了解js脚本。 - -(7)代码里有注释,便于理解。 - -### 2、安装 -(1)获取 [腾讯翻译API](https://cloud.tencent.com/product/tmt) 密钥(API接口说明:[腾讯翻译API文档](https://cloud.tencent.com/document/api/551/15619)) - -在浏览器搜索   “腾讯机器翻译”   或点击   [腾讯翻译](https://cloud.tencent.com/product/tmt)   前往, - -登录自己的账号后依次点击  头像  >> 访问管理  >> API密钥管理 >> 创建密钥 >> 复制密钥  - - - -(2) 安装   腾讯翻译SDK  (更多安装介绍:[腾讯翻译SDK文档](https://cloud.tencent.com/document/sdk/Python))。 - -请先确定你使用的 ComfyUI 的 Python 目录在哪里,在终端中切换到该目录下,输入以下命令: - - ``` - pip install --upgrade tencentcloud-sdk-python - ``` -或者使用国内镜像 - ``` - pip install -i https://mirrors.tencent.com/pypi/simple/ --upgrade tencentcloud-sdk-python - ``` - -(勿将SDK添加错位置,否则无效) - -(3)添加该节点到 ComfyUI - - 1) 克隆本项目 +####  1) 方式一:克隆本项目 ``` cd custom_nodes ``` ``` - git clone https://github.com/TFL-TFL/ComfyUI_Text_Translation_zh_CN.git + git clone https://github.com/TFL-TFL/ComfyUI_Text_Translation.git ``` - 2) 直接下载ZIP +####  2) 方式二:直接下载ZIP 下载解压后放入`custom_nodes`内 +### (2)配置环境 + ``` + cd ComfyUI_Text_Translation + ``` -(4)配置密钥 +如果你下载的是 ComfyUI 的 便携版本,可直接执行下面代码,否则请将 `requirements.txt` 内的依赖安装到对应位置。 + ``` + ..\..\..\python_embeded\python.exe -s -m pip install -r requirements.txt + ``` +#### (3)更改 翻译平台、翻译语言 +截止 [Translators](https://github.com/UlionTse/translators) 版本:v5.9.1 支持如下翻译平台: - 1) 直接写在代码里,在   .custom_nodes/ComfyUI_Text_Translation_zh_CN/node/tencent_translation.py   文件里, -找到`cred = credential.Credential("secretId", "secretKey")`这里填上你的密钥( "secretId"  和  "secretKey" )该方式有泄露密钥的风险,不推荐,但简单。 +``` +alibaba -- 阿里巴巴(Alibaba) +apertium -- Apertium +argos -- Argos +baidu -- 百度(Baidu) +bing -- 必应(Bing) +caiyun -- 彩云(Caiyun) +cloudTranslation -- 云翻译(CloudTranslation) +deepl -- DeepL +elia -- Elia +google -- 谷歌(Google) +hujiang -- 沪江(Hujiang) +iciba -- 金山词霸(ICIBA) +iflytek -- 讯飞(iFlytek) +iflyrec -- 讯飞语音识别(iFlyrec) +itranslate -- iTranslate +judic -- Judic +languageWire -- LanguageWire +lingvanex -- Lingvanex +niutrans -- 牛翻译(Niutrans) +mglip -- Mglip +mirai -- Mirai +modernMt -- ModernMT +myMemory -- MyMemory +papago -- Papago +qqFanyi -- QQ翻译(QQFanyi) +qqTranSmart -- QQ智能翻译(QQTranSmart) +reverso -- Reverso +sogou -- 搜狗(Sogou) +sysTran -- Systran +tilde -- Tilde +translateCom -- Translate.com +translateMe -- TranslateMe +utibet -- UTibet +volcEngine -- VolcEngine +yandex -- Yandex +yeekit -- Yeekit +youdao -- 有道(Youdao) +``` + 直接写在代码里,在   .custom_nodes/ComfyUI_Text_Translation/nodes/text.py   文件的如下位置修改: + - +#### (4)修改节点显示的语言 +直接写在代码里,在   .custom_nodes/ComfyUI_Text_Translation/nodes/text.py   文件的 开头和 结尾 位置修改: - 2) 放在环境变量里,添加`TENCENTCLOUD_SECRET_ID`和`TENCENTCLOUD_SECRET_KEY`,稍微多些步骤,但安全。(请在启动ComfyUI前设置好,若已启动 -需要重启 ComfyUI) +修改节点类别: - + -可在终端使用 `echo %环境变量名称%` 查看环境变量是否设置正确。 +修改节点名称: -(5)重启ComfyUI + + +#### (5)重启ComfyUI 所有的步骤都操作完啦,可以愉快的创作呦! ### 4、更新日志 -2023-8-27 +#### 2024-05-19 +1、弃用腾讯翻译API,简化配置,使用 [Translators](https://github.com/UlionTse/translators) 提供的方式进行翻译,无需申请密钥了。 + +2、完全重写节点、新增或修改 “文本翻译”、“文本翻译 V2”、 “获取翻译平台列表”、“文本”、“文本开关”、“文本联接”节点。 +2、完全重写节点、新增或修改 “文本翻译”、“文本翻译 V2”、 “获取翻译平台列表”、“文本”、“文本开关”、“文本联接”节点。 + +#### 2023-08-27  去除“翻译开关”的默认值,以便适配 [StableSwarmUI](https://github.com/Stability-AI/StableSwarmUI) ,不然该开关的值会由“可选择类型”变成“要输入类型的文字”。 -2023-8-22 +#### 2023-08-22  初始提交,节点采用了[腾讯翻译API](https://cloud.tencent.com/product/tmt)进行文本翻译。 -### 5、其他 -我对比了阿里、百度、有道、腾讯这些翻译API,其中腾讯每月免费可用的量是最大的,所以选择用它。 - -至于为什么没有选择免费的API,只是觉得这些API稳定性稍差,便没有考虑。 - - - - - \ No newline at end of file diff --git a/__init__.py b/__init__.py index 0eae08a..32db998 100644 --- a/__init__.py +++ b/__init__.py @@ -1,14 +1,19 @@ -from .node.tencent_translation import * +import os +import glob +import importlib -__version__ = "1.0.0" +node_list = [os.path.splitext(os.path.basename(f))[0] for f in glob.glob(os.path.join(os.path.dirname(__file__), "nodes", "*.py"))] -NODE_CLASS_MAPPINGS = { - "TencentTextTranslation": TencentTextTranslation, -} +NODE_CLASS_MAPPINGS = {} +NODE_DISPLAY_NAME_MAPPINGS = {} -NODE_DISPLAY_NAME_MAPPINGS = { - "TencentTextTranslation": "文本翻译", -} +for module_name in node_list: + if module_name != "__init__": + imported_module = importlib.import_module(".nodes.{}".format(module_name), __name__) + NODE_CLASS_MAPPINGS = {**NODE_CLASS_MAPPINGS, **imported_module.NODE_CLASS_MAPPINGS} + NODE_DISPLAY_NAME_MAPPINGS = {**NODE_DISPLAY_NAME_MAPPINGS, **imported_module.NODE_DISPLAY_NAME_MAPPINGS} -__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS'] + +WEB_DIRECTORY = "./js" +__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS","WEB_DIRECTORY"] diff --git a/images/Get_Translator.png b/images/Get_Translator.png new file mode 100644 index 0000000..7bc2ca1 Binary files /dev/null and b/images/Get_Translator.png differ diff --git a/images/Get_Translator_a.png b/images/Get_Translator_a.png new file mode 100644 index 0000000..134701f Binary files /dev/null and b/images/Get_Translator_a.png differ diff --git a/images/Text.png b/images/Text.png new file mode 100644 index 0000000..232e43e Binary files /dev/null and b/images/Text.png differ diff --git a/images/Text_Concatenate.png b/images/Text_Concatenate.png new file mode 100644 index 0000000..7d7d860 Binary files /dev/null and b/images/Text_Concatenate.png differ diff --git a/images/Text_Concatenate2.png b/images/Text_Concatenate2.png new file mode 100644 index 0000000..9883d2a Binary files /dev/null and b/images/Text_Concatenate2.png differ diff --git a/images/Text_Switch.png b/images/Text_Switch.png new file mode 100644 index 0000000..d79b894 Binary files /dev/null and b/images/Text_Switch.png differ diff --git a/images/Text_Translation.png b/images/Text_Translation.png new file mode 100644 index 0000000..86ad529 Binary files /dev/null and b/images/Text_Translation.png differ diff --git a/images/Text_Translation_V2.png b/images/Text_Translation_V2.png new file mode 100644 index 0000000..d4cf327 Binary files /dev/null and b/images/Text_Translation_V2.png differ diff --git a/images/Text_Translation_V2_a.png b/images/Text_Translation_V2_a.png new file mode 100644 index 0000000..f8d4b3b Binary files /dev/null and b/images/Text_Translation_V2_a.png differ diff --git a/images/Text_Translation_V2_b.png b/images/Text_Translation_V2_b.png new file mode 100644 index 0000000..25f2fd8 Binary files /dev/null and b/images/Text_Translation_V2_b.png differ diff --git a/images/Text_Translation_V2_s.png b/images/Text_Translation_V2_s.png new file mode 100644 index 0000000..0800cb4 Binary files /dev/null and b/images/Text_Translation_V2_s.png differ diff --git a/images/image1.png b/images/image1.png deleted file mode 100644 index af6e780..0000000 Binary files a/images/image1.png and /dev/null differ diff --git a/images/image10.png b/images/image10.png deleted file mode 100644 index 269da54..0000000 Binary files a/images/image10.png and /dev/null differ diff --git a/images/image11.png b/images/image11.png deleted file mode 100644 index cdcce7c..0000000 Binary files a/images/image11.png and /dev/null differ diff --git a/images/image2.png b/images/image2.png deleted file mode 100644 index 50ec76c..0000000 Binary files a/images/image2.png and /dev/null differ diff --git a/images/image3.png b/images/image3.png deleted file mode 100644 index b5eea8c..0000000 Binary files a/images/image3.png and /dev/null differ diff --git a/images/image4.png b/images/image4.png deleted file mode 100644 index bf4d1eb..0000000 Binary files a/images/image4.png and /dev/null differ diff --git a/images/image5.png b/images/image5.png deleted file mode 100644 index f9dfa80..0000000 Binary files a/images/image5.png and /dev/null differ diff --git a/images/image6.png b/images/image6.png deleted file mode 100644 index 28fd1f0..0000000 Binary files a/images/image6.png and /dev/null differ diff --git a/images/image7.png b/images/image7.png deleted file mode 100644 index 52e319a..0000000 Binary files a/images/image7.png and /dev/null differ diff --git a/images/image8.png b/images/image8.png deleted file mode 100644 index 708f656..0000000 Binary files a/images/image8.png and /dev/null differ diff --git a/images/image9.png b/images/image9.png deleted file mode 100644 index 3da8873..0000000 Binary files a/images/image9.png and /dev/null differ diff --git a/js/text.js b/js/text.js new file mode 100644 index 0000000..946d6a1 --- /dev/null +++ b/js/text.js @@ -0,0 +1,77 @@ +import {app } from "../../scripts/app.js"; +import { log, dynamic_connection, findWidgetByName, toggleWidget } from './ulits.js' + +app.registerExtension({ + name: "TextTranslation.text", + + async beforeRegisterNodeDef(nodeType, nodeData, app) { + + switch(nodeData.name){ + case 'Text_Concatenate': + text_concatenate_widget(nodeType, nodeData, app); + break; + + case 'Text_Translation_V2': + text_translation_v2_widget(nodeType, nodeData, app); + break; + } + }, +}); + +function text_concatenate_widget(nodeType, nodeData, app) { + var input_name = "text"; + + const onNodeCreated = nodeType.prototype.onNodeCreated; + nodeType.prototype.onNodeCreated = function () { + const onc = onNodeCreated?.apply(this, arguments); + this.addInput(`${input_name}`, "STRING"); + return onc; + }; + + const onConnectionsChange = nodeType.prototype.onConnectionsChange; + nodeType.prototype.onConnectionsChange = function (type, index, connected, link_info) { + if(!link_info) + return; + + const occ = onConnectionsChange + ? onConnectionsChange.apply(this, arguments) + : undefined + + // log("输入类型:" +this.inputs[0].type); + dynamic_connection(this, index, connected, input_name, this.inputs[0].type ); + return occ + } +} + +function text_translation_v2_widget(nodeType, nodeData, app) { + const onNodeCreated = nodeType.prototype.onNodeCreated; + nodeType.prototype.onNodeCreated = function () { + const onc = onNodeCreated?.apply(this, arguments); + + let node = this; + + toggleWidget(node, findWidgetByName(node, 'translator'), false); + toggleWidget(node, findWidgetByName(node, 'source_language'), false); + toggleWidget(node, findWidgetByName(node, 'target_language'), false); + + node.addWidget("button", "Show / Hide button", "button_show", function(value, widget, node){ + + let is_show = null + if(this.value === "button_show") { + is_show = true; + } else if (this.value === "button_hide") { + is_show = false; + } + + toggleWidget(node, findWidgetByName(node, 'translator'), is_show) + toggleWidget(node, findWidgetByName(node, 'source_language'), is_show) + toggleWidget(node, findWidgetByName(node, 'target_language'), is_show) + this.value = is_show ? "button_hide" : "button_show"; + app.graph.setDirtyCanvas(true); + + }); + return onc; + }; + +} + diff --git a/js/ulits.js b/js/ulits.js new file mode 100644 index 0000000..60f0230 --- /dev/null +++ b/js/ulits.js @@ -0,0 +1,82 @@ + +export const isPropertiesWidgetShow = (widget_name) => { + if(properties_widget[widget_name]){ + return properties_widget[widget.name].value.includes('_hide'); + }else{ + return false; + } +}; + + +let origProps = {}; +export const findWidgetByName = (node, name) => node.widgets.find((w) => w.name === name); + +export const doesInputWithNameExist = (node, name) => node.inputs ? node.inputs.some((input) => input.name === name) : false; + +export function updateNodeHeight(node) {node.setSize([node.size[0], node.computeSize()[1]]);} + +export function toggleWidget(node, widget, show = false, suffix = "") { + if (!widget || doesInputWithNameExist(node, widget.name)) return; + if (!origProps[widget.name]) { + origProps[widget.name] = { origType: widget.type, origComputeSize: widget.computeSize }; + } + const origSize = node.size; + + widget.type = show ? origProps[widget.name].origType : "easyHidden" + suffix; + widget.computeSize = show ? origProps[widget.name].origComputeSize : () => [0, -4]; + + widget.linkedWidgets?.forEach(w => toggleWidget(node, w, ":" + widget.name, show)); + + const height = show ? Math.max(node.computeSize()[1], origSize[1]) : node.size[1]; + node.setSize([node.size[0], height]); +} + + +export const dynamic_connection = ( + node, + index, + connected, + connectionPrefix = 'input_', + connectionType = 'PSDLAYER' + ) => { + + if (!connected && (node.inputs.length > 1)) { + const stackTrace = new Error().stack; + for (let i = 0; i < node.inputs.length -1; i++) { + if(!stackTrace.includes('LGraphNode.prototype.connect') && + !stackTrace.includes('LGraphNode.connect') && + !stackTrace.includes('loadGraphData') && + !node.inputs[i].link) { + node.removeInput(i); + } + } + } + + let last_slot = node.inputs[node.inputs.length - 1]; + if (last_slot.link != undefined) { + node.addInput(`${connectionPrefix}`, connectionType); + } + + if (connected && (node.inputs.length > 1)) { + const stackTrace = new Error().stack; + for (let i = 0; i < node.inputs.length -1; i++) { + if(!stackTrace.includes('LGraphNode.prototype.connect') && + !stackTrace.includes('LGraphNode.connect') && + !stackTrace.includes('loadGraphData') && + !node.inputs[i].link) { + node.removeInput(i); + } + } + } + + if ((node.inputs.length > 1)) { + for (let i = 0; i < node.inputs.length -1; i++) { + node.inputs[i].name = `${connectionPrefix}${i + 1}` + node.inputs[i].label = `${connectionPrefix}${i + 1}` + } + } +} + +export const log = (...args) => { + console.debug(...args) + } \ No newline at end of file diff --git a/node/tencent_translation.py b/node/tencent_translation.py deleted file mode 100644 index 42e7fc7..0000000 --- a/node/tencent_translation.py +++ /dev/null @@ -1,103 +0,0 @@ -import json -import re -import os -from tencentcloud.common import credential -from tencentcloud.common.profile.client_profile import ClientProfile -from tencentcloud.common.profile.http_profile import HttpProfile -from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException -from tencentcloud.tmt.v20180321 import tmt_client, models - -# 腾讯翻译安装 Python SDK https://cloud.tencent.com/document/sdk/Python -# pip install --upgrade tencentcloud-sdk-python - -# 腾讯翻译文档 https://cloud.tencent.com/document/api/551/15619 - - -# region 腾讯翻译全局对象 起始 -httpProfile = HttpProfile() -httpProfile.endpoint = "tmt.tencentcloudapi.com" -clientProfile = ClientProfile() -clientProfile.httpProfile = httpProfile -# 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取 -# 有两种方式设置密钥 -# (1)硬编码密钥到代码中,这有可能随代码泄露而暴露,有安全隐患,不推荐 (简单) -# cred = credential.Credential("secretId", "secretKey") -# (2)密钥设置在环境变量中,推荐 (稍微多些步骤,请在开启ComfyUI前设置好,若已开启需要重启ComfyUI) -# 可在终端使用 echo %环境变量名称% 查看是否正确设置 -cred = credential.Credential( - os.environ.get("TENCENTCLOUD_SECRET_ID"), - os.environ.get("TENCENTCLOUD_SECRET_KEY")) -# 操作的资源所属的地域 如、ap-beijing、ap-chengdu、ap-chongqing、ap-guangzhou、ap-shanghai -# 可以选择离自己近的地域,更多详情见 https://cloud.tencent.com/document/api/551/15615#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8 -tmt_client_instance = tmt_client.TmtClient(cred, "ap-guangzhou", clientProfile) -# endregion - - -# 调用腾讯翻译API -def tencent_api(text_to_translate): - try: - req = models.TextTranslateRequest() - params = { - "SourceText": text_to_translate, - "Source": "auto", # 翻译前的语言类型,默认 自动(auto) - "Target": "en", # 翻译后的语言类型,默认 英语(en) - "ProjectId": 0, - } - req.from_json_string(json.dumps(params)) - resp = tmt_client_instance.TextTranslate(req) - return resp.TargetText - except TencentCloudSDKException as err: - print("文本翻译错误:" + err) - return "" - - -# 判断是否包含中文 -def contains_chinese(text): - pattern = re.compile(r'[\u4e00-\u9fa5]') - return bool(pattern.search(text)) - - -class TencentTextTranslation: - @classmethod - def INPUT_TYPES(s): - return { - "required": { - "text_trans": ("STRING", {"multiline": True}), - "text_normal": ("STRING", {"multiline": True}), - "trans_switch": (["enabled", "disabled"],), - }, - } - - RETURN_TYPES = ("STRING",) - FUNCTION = "translation" - CATEGORY = "utils" - - # trans_switch 翻译功能的开关,enabled 开启;disabled 关闭 - def translation(self, text_trans, text_normal, trans_switch, ): - if text_trans == "undefined": - text_trans = "" - if text_normal == "undefined": - - text_normal = "" - is_enabled = trans_switch == "enabled" - target_text = "" - if is_enabled and contains_chinese(text_trans): - target_text = tencent_api(text_trans) - # 在终端打印翻译后的内容,不需要可删除或注释 - print("文本翻译结果: " + target_text) - else: - target_text = text_trans - output_text = ", ".join(filter(None, [target_text, text_normal])) - output_text = output_text.replace(',', ',').replace('。', ',').replace(" ", " ").replace(" ,", ",").replace(",,", ",") - # 在终端打印翻译后的内容,不需要可删除或注释 - print("文本最终结果: " + output_text) - return (output_text,) - - -# NODE_CLASS_MAPPINGS = { -# "TencentTranslation": TencentTextTranslation, -# } -# -# NODE_DISPLAY_NAME_MAPPINGS = { -# "TencentTranslation": "文本翻译", -# } diff --git a/nodes/text.py b/nodes/text.py new file mode 100644 index 0000000..635304f --- /dev/null +++ b/nodes/text.py @@ -0,0 +1,201 @@ + +from ..utils.translation import translators, get_translator + +# 适应不同地区的人使用该节点,当前默认类别名为英文,按需自行更改 +# Adapt to people from different regions using this node. +# The current default class alias is in English and can be changed as needed + +# CATEGORY_NAME = "文本翻译" +CATEGORY_NAME = "Text Translation" + +class Text: + def __init__(self): + pass + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "text": ("STRING", {"multiline": True}), + }, + } + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("string",) + FUNCTION = "func" + #OUTPUT_NODE = False + CATEGORY = CATEGORY_NAME + + def func(self, text): + return (text,) + + +class Text_Translation: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "trans_switch": ("BOOLEAN", {"default": True, "label_on": "on", "label_off": "off"}), + "trans_text": ("STRING", {"multiline": True}), + }, + } + + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("string",) + + FUNCTION = "func" + + #OUTPUT_NODE = False + + CATEGORY = CATEGORY_NAME + + def func(self, trans_switch, trans_text): + output_text = "" + if trans_switch: + output_text = translators(text = trans_text) + else: + output_text = trans_text + return (output_text,) + +class Text_Translation_V2: + def __init__(self): + pass + + # 截止 [Translators](https://github.com/UlionTse/translators) 版本:v5.9.1 支持如下翻译平台: + # alibaba, apertium, argos, baidu, bing, caiyun, cloudTranslation, deepl, elia, + # google, hujiang, iciba, iflytek, iflyrec, itranslate, judic, languageWire, lingvanex, + # niutrans, mglip, mirai, modernMt, myMemory, papago, qqFanyi, qqTranSmart, reverso, + # sogou, sysTran, tilde, translateCom, translateMe, utibet, volcEngine, yandex, yeekit, youdao + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "trans_switch": ("BOOLEAN", {"default": True, "label_on": "on", "label_off": "off"}), + "trans_text": ("STRING", {"multiline": True}), + "translator": (["alibaba", "bing", "google", ],), #直接再此添加 翻译平台 + "source_language": (["auto", "zh", "en", ],), #直接再此添加 相应支持的源语言 + "target_language": (["en", "zh", ],), #直接再此添加 相应支持的目标语言 + }, + } + + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("string",) + + FUNCTION = "func" + + #OUTPUT_NODE = False + + CATEGORY = CATEGORY_NAME + + def func(self, trans_switch, trans_text, translator, source_language, target_language): + output_text = "" + if trans_switch: + output_text = translators(text = trans_text, translator = translator, source_language = source_language, target_language = target_language) + else: + output_text = trans_text + return (output_text,) + +class Get_Translator: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { }, + } + + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("string",) + + FUNCTION = "func" + + #OUTPUT_NODE = False + + CATEGORY = CATEGORY_NAME + + def func(self): + translator = get_translator() + return (translator,) + +class Text_Switch: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "switch": ("BOOLEAN", {"default": True, "label_on": "on", "label_off": "off"}), + "text": ("STRING", {"forceInput": True}) + }, + } + + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("string",) + + FUNCTION = "func" + + #OUTPUT_NODE = False + + CATEGORY = CATEGORY_NAME + + def func(self, switch, text): + output_text = "" + if switch: + output_text = text + return (output_text,) + +class Text_Concatenate: + @classmethod + def INPUT_TYPES(s): + return {"required": {}} + + RETURN_TYPES = ("STRING",) + FUNCTION = "func" + + CATEGORY = CATEGORY_NAME + + def func(self, **kwargs): + text = "" + for k, v in kwargs.items(): + text = text + v + return (text, ) + + + +NODE_CLASS_MAPPINGS = { + "Text": Text, + "Text_Translation": Text_Translation, + "Text_Translation_V2": Text_Translation_V2, + "Text_Switch": Text_Switch, + "Text_Concatenate": Text_Concatenate, + "Get_Translator": Get_Translator, +} + + +# 适应不同地区的人使用该节点,当前默认节点名为英文,按需自行更改 +# Adapt to people from different regions using this node. +# The current default node name is English and can be changed as needed + +# NODE_DISPLAY_NAME_MAPPINGS = { +# "Text": "文本", +# "Text_Translation": "文本翻译", +# "Text_Translation_V2": "文本翻译V2", +# "Text_Switch": "文本开关", +# "Text_Concatenate": "文本联接", +# "Get_Translator": "获取翻译平台", +# } + +NODE_DISPLAY_NAME_MAPPINGS = { + "Text": "Text", + "Text_Translation": "Text Translation", + "Text_Translation_V2": "Text Translation V2", + "Text_Switch": "Text Switch", + "Text_Concatenate": "Text Concatenate", + "Get_Translator": "Get Translator", +} + + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..71bc40a --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +translators \ No newline at end of file diff --git a/utils/string.py b/utils/string.py new file mode 100644 index 0000000..527f1e4 --- /dev/null +++ b/utils/string.py @@ -0,0 +1,14 @@ + + +# 判断是否包含中文 +def has_chinese_character(string): + """ + 判断字符串中是否包含中文字符 + :param string: 待判断的字符串 + :return: True如果字符串中至少包含一个中文字符,否则返回False + """ + for char in string: + if '\u4e00' <= char <= '\u9fff': + return True + return False + diff --git a/utils/translation.py b/utils/translation.py new file mode 100644 index 0000000..c3b95df --- /dev/null +++ b/utils/translation.py @@ -0,0 +1,22 @@ + +def translators(text : str , translator: str = "bing", source_language = "auto", target_language = "en", timeout: float = 10.0): + if not text: + return "" + try: + import translators + result = translators.translate_text(query_text = text, translator = translator,from_language = source_language, to_language= target_language, timeout= timeout) + return result + except Exception as e: + raise Exception(f"Error: Translation failed , Message : {e}") + +def get_translator(): + try: + import translators + translators_list = translators.translators_pool + result = '\n'.join(translators_list) + print(f"Text Translation translator: \n{result}") + return result + + except Exception as e: + raise Exception(f"Error: Translation failed , Message : {e}") +