📝💬AGSoft Dialogue Keeper
Machine translation is bad at dialogue — this node puts the original lines back
- text
The one trick it exists for
Run a story, a roleplay log, or a novel chapter through any machine translator and you get clean prose - and flat, lifeless dialogue. Every character suddenly talks like the same translation-service native speaker, and the names and turns of phrase you actually wanted kept in the original language are gone. 📝💬AGSoft Dialogue Keeper is a post-processor that fixes exactly that: give it the original text and the translation, and it finds every quoted or tagged line of dialogue and swaps the original wording back into the translated result. It's a find-and-replace surgeon, not a translator - no model, no API key, no GPU, runs in a millisecond.
It ships in the comfyui-AGSoft pack, which is built around a Russian text-and-video workflow (the AGSoft Translate node alongside it translates via free web services). If you're localizing text where dialogue should stay in the source language, this is the node the pack was really made for. If you only translate plain paragraphs, you don't need it.
How it works
Under the hood it's plain string surgery with regex - zero magic. It scans original_text for spans wrapped in your chosen template tags and/or quote-pair markers, scans translated_text with the same pattern, then replaces each translated span with the matching original one. When both sides have the same number of dialogues it just pairs them in order. When counts differ (the translator merged or dropped a line), it warns in the console and tries to align by marker type instead - decent, but imperfect.
The one design detail that looks like a bug but isn't: the template dropdown shows ‹d›....‹/d› with fancy single quotes instead of <d>....</d>. ComfyUI's dropdown eats angle brackets as HTML, so the author renders them HTML-safe. It means the same thing.
The inputs that matter
You only set two things for the basic case:
- original_text (required) - the source text before translation. The node hunts here for the dialogue spans to copy back.
- translated_text (required) - the translation. Everything stays as-is except the spans it recognizes as dialogue.
Then the marking scheme, which is where the real choices live:
- template - the dropdown. Default is
‹d›....‹/d› + markers, i.e. look for<d>...</d>tags and any quote pair.[Russian]variants tell you the intent: keep Russian dialogue in the English translation.markers onlyskips tags entirely;Custom tagsuses your owntag_start/tag_endpair like[dialog]…[/dialog]. - markers - the list of quote-pair delimiters. Defaults to the full quote zoo (
" ",« »,“ ”,‘ ’,„ “, …) so whatever curly quotes your source uses get caught. You can add multi-character markers like<< >>(or<<|>>when you need an explicit open/close).
The single output is text - wire it into a Show Text node (this pack has 📝AGSoft Show Text and 👁️ AGSoft Show Any) or into whatever downstream step consumes your patched translation.
Install
The pack installs like any custom node, and this particular node has no extra dependencies - the whole thing is standard Python. The pack's requirements.txt only lists translators, and that's for AGSoft Translate, not this node.
cd ComfyUI/custom_nodes
git clone https://github.com/Art-xmaster/comfyui-AGSoft.git
Then restart ComfyUI. Easier: ComfyUI Manager → search "comfyui-AGSoft" → Install. You'll find the node under AGSoft → Text. Note the README is mostly Russian, and extended docs live on the author's Telegram channel - that's a heads-up, not a blocker; the tooltips in the node are bilingual.
Where people get burned
Tag mode assumes your translation step keeps the <d>...</d> tags intact. Free web translators often strip or mangle markup, so if you get an unchanged output plus a console warning about "No dialogues matched," that's usually why - check whether the tags survived in the translated text, or switch to markers only.
Quote mode has its own trap: if the translator "normalized" the quote characters (turned « » into " "), the original and translated spans no longer share a marker type and alignment falls back to fuzzy matching. Keep your markers list covering what the translation actually outputs, not just what your source uses.
If either input is empty, or nothing matches, or anything throws, the node fails safe and returns the translation unchanged - which is polite, but means a silent no-op is the symptom to watch for when it looks like the node "did nothing." Check the ComfyUI console for the [AGSoftDialogueKeeper] warning lines; they tell you exactly which branch it took.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| original_text | STRING | Field original_text: original text before translation. The node searches this text for dialogues inside selected tags/markers and copies them back into the translated result. --- Поле original_text: исходный текст до перевода. Нода ищет в нём диалоги внутри выбранных тегов/маркеров и копирует их обратно в переведённый результат. | |
| translated_text | STRING | Field translated_text: translated text. The main English translation is kept, but fragments matching dialogues are replaced by the original Russian fragments from original_text. --- Поле translated_text: переведённый текст. Основной английский перевод сохраняется, но фрагменты, признанные диалогами, заменяются русскими фрагментами из original_text. | |
| templateopt | COMBO | ‹d›....‹/d› + markers | Field template: dialogue marker template. The menu shows ‹d› instead of <d> (HTML-safe notation). Options: ‹d›....‹/d› = <d>....</d>, ‹d›[Russian]....‹/d› = <d>[Russian]....</d>, [d]....[/d], markers only (all symbol pairs from the markers field), combinations with + markers, Custom tags (uses tag_start, tag_end and markers). --- Поле template: шаблон маркеров диалогов. В меню ‹d› вместо <d> (HTML-безопасная запись). Варианты: ‹d›....‹/d› = <d>....</d>, ‹d›[Russian]....‹/d› = <d>[Russian]....</d>, [d]....[/d], markers only (все пары символов из поля markers), комбинации с + markers, Custom tags (использует tag_start, tag_end и markers). |
| tag_startopt | STRING | <d> | Field tag_start: opening tag for Custom tags mode. Examples: <d>, [dialog], {{say}}. Works only together with tag_end. In preset templates this field is ignored. --- Поле tag_start: открывающий тег для режима Custom tags. Примеры: <d>, [dialog], {{say}}. Работает только вместе с tag_end. В пресетных шаблонах это поле игнорируется. |
| tag_endopt | STRING | </d> | Field tag_end: closing tag for Custom tags mode. Examples: </d>, [/dialog], {{/say}}. Works only together with tag_start. In preset templates this field is ignored. --- Поле tag_end: закрывающий тег для режима Custom tags. Примеры: </d>, [/dialog], {{/say}}. Работает только вместе с tag_start. В пресетных шаблонах это поле игнорируется. |
| markersopt | STRING | " ", « », “ ”, ‘ ’, „ “, ‚ ‘, ‹ › | Field markers: list of symbol pairs that wrap dialogues. By default it contains quotes " ", « », “ ”, ‘ ’, but you can put any symbols or multi-character markers. Used by templates with + markers and by Custom tags. Write pairs separated by commas, spaces or new lines; for multi-character markers use space or vertical bar: << >> or <<|>>. In Custom tags an empty field means no markers; in preset templates an empty field uses the default list. --- Поле markers: список пар символов, оборачивающих диалоги. По умолчанию содержит кавычки " ", « », “ ”, ‘ ’, но можно вставлять любые символы и многозначные маркеры. Используется шаблонами с + markers и режимом Custom tags. Пишите пары через запятую, пробел или с новой строки; для многозначных маркеров используйте пробел или вертикальную черту: << >> или <<|>>. В Custom tags пустое поле означает без маркеров; в пресетных шаблонах пустое поле использует стандартный список. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| text | STRING | — |