diff --git a/__init__.py b/__init__.py index ac09c70..913516d 100644 --- a/__init__.py +++ b/__init__.py @@ -1,14 +1,19 @@ from .nodes.string_formatter import StringFormatter from .nodes.string_list import StringList +from .nodes.show_translate_string import ShowTranslateString NODE_CLASS_MAPPINGS = { "StringFormatter": StringFormatter, - "StringList": StringList + "StringList": StringList, + "ShowTranslateString": ShowTranslateString } NODE_DISPLAY_NAME_MAPPINGS = { "StringFormatter": "String Formatter", - "StringList": "String List" + "StringList": "String List", + "ShowTranslateString": "Show Translate String" } -__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS'] \ No newline at end of file +WEB_DIRECTORY = "./web" + +__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', 'WEB_DIRECTORY'] \ No newline at end of file diff --git a/nodes/show_translate_string.py b/nodes/show_translate_string.py new file mode 100644 index 0000000..3a5633b --- /dev/null +++ b/nodes/show_translate_string.py @@ -0,0 +1,36 @@ +from ..translation_utils import TranslationUtils + +class ShowTranslateString: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "input_strings": ("STRING", { + "forceInput": True + }), + "target_language": (["en", "zh", "es", "fr", "de", "ja", "ko"], { + "default": "zh" + }) + } + } + + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("translated_strings",) + OUTPUT_IS_LIST = (True,) + FUNCTION = "translate" + CATEGORY = "String Helper" + + def translate(self, input_strings, target_language): + """Translate the input strings to the target language using Bing translator""" + # Ensure input_strings is a list + if isinstance(input_strings, str): + strings_to_translate = [input_strings] + elif isinstance(input_strings, (list, tuple)): + strings_to_translate = [str(s) for s in input_strings] + else: + strings_to_translate = [str(input_strings)] + + #逐个翻译每个字符串 + translated_strings = TranslationUtils.translate_with_length_check(strings_to_translate, target_language) + + return {"ui": {"translated_strings": translated_strings}, "result": (translated_strings,)} diff --git a/nodes/string_list.py b/nodes/string_list.py index 75f8645..41651cf 100644 --- a/nodes/string_list.py +++ b/nodes/string_list.py @@ -1,5 +1,5 @@ import random -import translators.server as tss +from ..translation_utils import TranslationUtils class StringList: @classmethod @@ -84,7 +84,8 @@ class StringList: def translate_strings(self, strings): """Translate a list of strings to English using Bing translator with auto language detection""" try: - return [tss.bing(text, from_language='auto', to_language='en') for text in strings] + translated_strings = TranslationUtils.translate_with_length_check(strings, 'en') + return translated_strings except Exception as e: print(f"Translation error: {str(e)}") return strings diff --git a/translation_utils.py b/translation_utils.py new file mode 100644 index 0000000..3ecca96 --- /dev/null +++ b/translation_utils.py @@ -0,0 +1,49 @@ +import translators.server as tss +import re + +class TranslationUtils: + @staticmethod + def translate_with_length_check(strings, target_language): + translated_strings = [] + for text in strings: + # Check if the string is too long and split if necessary + if len(text) > 500: + parts = text.split('\n') # Split by paragraphs + temp_translated_strings = [] + for part in parts: + if len(part) > 500: + sub_parts = re.split(r'(?<=[.!?]) +', part) # Split long paragraphs by sentences + for sub_part in sub_parts: + if len(sub_part) > 500: + sub_sub_parts = [sub_part[i:i+500] for i in range(0, len(sub_part), 500)] + for sub_sub_part in sub_sub_parts: + try: + translated_string = tss.bing(sub_sub_part, from_language='auto', to_language=target_language) + temp_translated_strings.append(translated_string) + except Exception as e: + temp_translated_strings.append(f"Translation error: {str(e)}") + else: + try: + translated_string = tss.bing(sub_part, from_language='auto', to_language=target_language) + temp_translated_strings.append(translated_string) + except Exception as e: + temp_translated_strings.append(f"Translation error: {str(e)}") + else: + try: + translated_string = tss.bing(part, from_language='auto', to_language=target_language) + temp_translated_strings.append(translated_string) + except Exception as e: + temp_translated_strings.append(f"Translation error: {str(e)}") + # Merge translated strings if they were split + if len(temp_translated_strings) > 1: + translated_strings.append(' '.join(temp_translated_strings)) + else: + translated_strings.append(temp_translated_strings[0] if temp_translated_strings else '') + else: + try: + translated_string = tss.bing(text, from_language='auto', to_language=target_language) + translated_strings.append(translated_string) + except Exception as e: + translated_strings.append(f"Translation error: {str(e)}") + + return translated_strings diff --git a/web/js/showTranslateString.js b/web/js/showTranslateString.js new file mode 100644 index 0000000..46e52b3 --- /dev/null +++ b/web/js/showTranslateString.js @@ -0,0 +1,66 @@ +import { app } from "../../../scripts/app.js"; +import { ComfyWidgets } from "../../../scripts/widgets.js"; + +app.registerExtension({ + name: "ComfyUI-String-Helper.ShowTranslateString", + async beforeRegisterNodeDef(nodeType, nodeData, app) { + if (nodeData.name === "ShowTranslateString") { + // 创建显示文本的函数 + function populate(texts) { + if (this.widgets) { + // 只删除带有特定前缀的 widgets + const widgetsToRemove = this.widgets.filter(w => w.name?.startsWith("translated_text_")); + for (const widget of widgetsToRemove) { + widget.onRemove?.(); + const index = this.widgets.indexOf(widget); + if (index > -1) { + this.widgets.splice(index, 1); + } + } + } + + // 确保 texts 是数组 + const textArray = Array.isArray(texts) ? texts : [texts]; + + // 为每个文本创建一个新的 widget,使用特定的命名前缀 + for (let i = 0; i < textArray.length; i++) { + const w = ComfyWidgets["STRING"](this, `translated_text_${i}`, ["STRING", { multiline: true }], app).widget; + w.inputEl.readOnly = true; + w.inputEl.style.opacity = 0.6; + w.value = textArray[i]; + } + + // 调整节点大小 + requestAnimationFrame(() => { + const sz = this.computeSize(); + if (sz[0] < this.size[0]) { + sz[0] = this.size[0]; + } + if (sz[1] < this.size[1]) { + sz[1] = this.size[1]; + } + this.onResize?.(sz); + app.graph.setDirtyCanvas(true, false); + }); + } + + // 处理执行结果 + const onExecuted = nodeType.prototype.onExecuted; + nodeType.prototype.onExecuted = function (message) { + onExecuted?.apply(this, arguments); + if (message.translated_strings) { + populate.call(this, message.translated_strings); + } + }; + + // 处理配置 + const onConfigure = nodeType.prototype.onConfigure; + nodeType.prototype.onConfigure = function () { + onConfigure?.apply(this, arguments); + if (this.widgets_values?.length) { + populate.call(this, this.widgets_values.slice(+this.widgets_values.length > 1)); + } + }; + } + } +});