diff --git a/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-node-chromium-2x-linux.png b/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-node-chromium-2x-linux.png index 1cc2c41c3..ba6cbe411 100644 Binary files a/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-node-chromium-2x-linux.png and b/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-node-chromium-2x-linux.png differ diff --git a/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-node-chromium-linux.png b/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-node-chromium-linux.png index 6e8e19c12..b8a8d877c 100644 Binary files a/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-node-chromium-linux.png and b/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-node-chromium-linux.png differ diff --git a/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-pinned-node-chromium-2x-linux.png b/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-pinned-node-chromium-2x-linux.png index a7d47069a..bceacd78e 100644 Binary files a/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-pinned-node-chromium-2x-linux.png and b/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-pinned-node-chromium-2x-linux.png differ diff --git a/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-pinned-node-chromium-linux.png b/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-pinned-node-chromium-linux.png index 8bb023ff7..4790b0c9e 100644 Binary files a/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-pinned-node-chromium-linux.png and b/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-pinned-node-chromium-linux.png differ diff --git a/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-unpinned-node-chromium-2x-linux.png b/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-unpinned-node-chromium-2x-linux.png index 1cc2c41c3..ba6cbe411 100644 Binary files a/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-unpinned-node-chromium-2x-linux.png and b/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-unpinned-node-chromium-2x-linux.png differ diff --git a/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-unpinned-node-chromium-linux.png b/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-unpinned-node-chromium-linux.png index 6e8e19c12..b8a8d877c 100644 Binary files a/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-unpinned-node-chromium-linux.png and b/browser_tests/rightClickMenu.spec.ts-snapshots/right-click-unpinned-node-chromium-linux.png differ diff --git a/src/components/graph/GraphCanvas.vue b/src/components/graph/GraphCanvas.vue index 6d2fe1670..d5eac8244 100644 --- a/src/components/graph/GraphCanvas.vue +++ b/src/components/graph/GraphCanvas.vue @@ -44,6 +44,7 @@ import SecondRowWorkflowTabs from '@/components/topbar/SecondRowWorkflowTabs.vue import { CORE_SETTINGS } from '@/constants/coreSettings' import { useCanvasDrop } from '@/hooks/canvasDropHooks' import { useGlobalLitegraph } from '@/hooks/litegraphHooks' +import { useContextMenuTranslation } from '@/hooks/translationHooks' import { useWorkflowPersistence } from '@/hooks/workflowPersistenceHooks' import { i18n } from '@/i18n' import { api } from '@/scripts/api' @@ -261,6 +262,8 @@ useCanvasDrop(canvasRef) onMounted(async () => { useGlobalLitegraph() + useContextMenuTranslation() + comfyApp.vueAppReady = true workspaceStore.spinner = true diff --git a/src/hooks/translationHooks.ts b/src/hooks/translationHooks.ts new file mode 100644 index 000000000..6eb3ffd57 --- /dev/null +++ b/src/hooks/translationHooks.ts @@ -0,0 +1,104 @@ +import type { + IContextMenuOptions, + IContextMenuValue, + INodeInputSlot, + IWidget +} from '@comfyorg/litegraph' +import { LGraphCanvas, LiteGraph } from '@comfyorg/litegraph' + +import { st, te } from '@/i18n' +import { normalizeI18nKey } from '@/utils/formatUtil' + +/** + * Add translation for litegraph context menu. + */ +export const useContextMenuTranslation = () => { + const f = LGraphCanvas.prototype.getCanvasMenuOptions + const getCanvasCenterMenuOptions = function ( + this: LGraphCanvas, + ...args: Parameters + ) { + const res = f.apply(this, args) as ReturnType + for (const item of res) { + if (item?.content) { + item.content = st(`contextMenu.${item.content}`, item.content) + } + } + return res + } + + LGraphCanvas.prototype.getCanvasMenuOptions = getCanvasCenterMenuOptions + + function translateMenus( + values: (IContextMenuValue | string)[] | undefined, + options: IContextMenuOptions + ) { + if (!values) return + const reInput = /Convert (.*) to input/ + const reWidget = /Convert (.*) to widget/ + const cvt = st('contextMenu.Convert ', 'Convert ') + const tinp = st('contextMenu. to input', ' to input') + const twgt = st('contextMenu. to widget', ' to widget') + for (const value of values) { + if (typeof value === 'string') continue + + translateMenus(value?.submenu?.options, options) + if (!value?.content) { + continue + } + if (te(`contextMenu.${value.content}`)) { + value.content = st(`contextMenu.${value.content}`, value.content) + } + + // for capture translation text of input and widget + const extraInfo: any = options.extra || options.parentMenu?.options?.extra + // widgets and inputs + const matchInput = value.content?.match(reInput) + if (matchInput) { + let match = matchInput[1] + extraInfo?.inputs?.find((i: INodeInputSlot) => { + if (i.name != match) return false + match = i.label ? i.label : i.name + }) + extraInfo?.widgets?.find((i: IWidget) => { + if (i.name != match) return false + match = i.label ? i.label : i.name + }) + value.content = cvt + match + tinp + continue + } + const matchWidget = value.content?.match(reWidget) + if (matchWidget) { + let match = matchWidget[1] + extraInfo?.inputs?.find((i: INodeInputSlot) => { + if (i.name != match) return false + match = i.label ? i.label : i.name + }) + extraInfo?.widgets?.find((i: IWidget) => { + if (i.name != match) return false + match = i.label ? i.label : i.name + }) + value.content = cvt + match + twgt + continue + } + } + } + + const OriginalContextMenu = LiteGraph.ContextMenu + function ContextMenu( + values: (IContextMenuValue | string)[], + options: IContextMenuOptions + ) { + if (options.title) { + options.title = st( + `nodeDefs.${normalizeI18nKey(options.title)}.display_name`, + options.title + ) + } + translateMenus(values, options) + const ctx = new OriginalContextMenu(values, options) + return ctx + } + + LiteGraph.ContextMenu = ContextMenu as unknown as typeof LiteGraph.ContextMenu +} diff --git a/src/locales/en/main.json b/src/locales/en/main.json index cdb2e2d35..fd1d0571c 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -101,6 +101,39 @@ "yellow": "Yellow", "custom": "Custom" }, + "contextMenu": { + "Inputs": "Inputs", + "Outputs": "Outputs", + "Properties": "Properties", + "Properties Panel": "Properties Panel", + "Title": "Title", + "Mode": "Mode", + "Resize": "Resize", + "Collapse": "Collapse", + "Expand": "Expand", + "Pin": "Pin", + "Unpin": "Unpin", + "Clone": "Clone", + "Remove": "Remove", + "Colors": "Colors", + "Shapes": "Shapes", + "Bypass": "Bypass", + "Copy (Clipspace)": "Copy (Clipspace)", + "Convert Widget to Input": "Convert Widget to Input", + "Convert Input to Widget": "Convert Input to Widget", + "Add Node": "Add Node", + "Add Group": "Add Group", + "Convert to Group Node": "Convert to Group Node", + "Manage Group Nodes": "Manage Group Nodes", + "Add Group For Selected Nodes": "Add Group For Selected Nodes", + "Save Selected as Template": "Save Selected as Template", + "Node Templates": "Node Templates", + "Manage": "Manage", + "Convert ": "Convert ", + " to input": " to input", + " to widget": " to widget", + "Search": "Search" + }, "icon": { "bookmark": "Bookmark", "folder": "Folder", diff --git a/src/locales/fr/main.json b/src/locales/fr/main.json index 881ccafe2..5fd169306 100644 --- a/src/locales/fr/main.json +++ b/src/locales/fr/main.json @@ -8,6 +8,39 @@ "red": "Rouge", "yellow": "Jaune" }, + "contextMenu": { + " to input": " en entrée", + " to widget": " en widget", + "Add Group": "Ajouter un Groupe", + "Add Group For Selected Nodes": "Ajouter un Groupe pour les Nœuds Sélectionnés", + "Add Node": "Ajouter un Nœud", + "Bypass": "Contourner", + "Clone": "Cloner", + "Collapse": "Réduire", + "Colors": "Couleurs", + "Convert ": "Convertir ", + "Convert Input to Widget": "Convertir l'Entrée en Widget", + "Convert Widget to Input": "Convertir le Widget en Entrée", + "Convert to Group Node": "Convertir en Nœud de Groupe", + "Copy (Clipspace)": "Copier (Clipspace)", + "Expand": "Développer", + "Inputs": "Entrées", + "Manage": "Gérer", + "Manage Group Nodes": "Gérer les Nœuds de Groupe", + "Mode": "Mode", + "Node Templates": "Modèles de Nœuds", + "Outputs": "Sorties", + "Pin": "Épingler", + "Properties": "Propriétés", + "Properties Panel": "Panneau des Propriétés", + "Remove": "Supprimer", + "Resize": "Redimensionner", + "Save Selected as Template": "Enregistrer la Sélection comme Modèle", + "Search": "Recherche", + "Shapes": "Formes", + "Title": "Titre", + "Unpin": "Désépingler" + }, "dataTypes": { "AUDIO": "AUDIO", "BOOLEAN": "BOOLEAN", diff --git a/src/locales/ja/main.json b/src/locales/ja/main.json index c225031e5..c9dd754fa 100644 --- a/src/locales/ja/main.json +++ b/src/locales/ja/main.json @@ -8,6 +8,39 @@ "red": "赤", "yellow": "黄色" }, + "contextMenu": { + " to input": " 入力へ", + " to widget": " ウィジェットへ", + "Add Group": "グループを追加", + "Add Group For Selected Nodes": "選択したノードのグループを追加", + "Add Node": "ノードを追加", + "Bypass": "バイパス", + "Clone": "クローン", + "Collapse": "折りたたむ", + "Colors": "色", + "Convert ": "変換 ", + "Convert Input to Widget": "入力をウィジェットに変換", + "Convert Widget to Input": "ウィジェットを入力に変換", + "Convert to Group Node": "グループノードに変換", + "Copy (Clipspace)": "コピー (Clipspace)", + "Expand": "展開", + "Inputs": "入力", + "Manage": "管理", + "Manage Group Nodes": "グループノードを管理", + "Mode": "モード", + "Node Templates": "ノードテンプレート", + "Outputs": "出力", + "Pin": "ピン", + "Properties": "プロパティ", + "Properties Panel": "プロパティパネル", + "Remove": "削除", + "Resize": "リサイズ", + "Save Selected as Template": "選択したものをテンプレートとして保存", + "Search": "検索", + "Shapes": "形", + "Title": "タイトル", + "Unpin": "ピン解除" + }, "dataTypes": { "AUDIO": "オーディオ", "BOOLEAN": "ブール", diff --git a/src/locales/ko/main.json b/src/locales/ko/main.json index 21451298e..b9e666a63 100644 --- a/src/locales/ko/main.json +++ b/src/locales/ko/main.json @@ -8,6 +8,39 @@ "red": "빨간색", "yellow": "노란색" }, + "contextMenu": { + " to input": " 입력으로", + " to widget": " 위젯으로", + "Add Group": "그룹 추가", + "Add Group For Selected Nodes": "선택된 노드에 대한 그룹 추가", + "Add Node": "노드 추가", + "Bypass": "우회", + "Clone": "복제", + "Collapse": "축소", + "Colors": "색상", + "Convert ": "변환 ", + "Convert Input to Widget": "입력을 위젯으로 변환", + "Convert Widget to Input": "위젯을 입력으로 변환", + "Convert to Group Node": "그룹 노드로 변환", + "Copy (Clipspace)": "복사 (Clipspace)", + "Expand": "확장", + "Inputs": "입력", + "Manage": "관리", + "Manage Group Nodes": "그룹 노드 관리", + "Mode": "모드", + "Node Templates": "노드 템플릿", + "Outputs": "출력", + "Pin": "고정", + "Properties": "속성", + "Properties Panel": "속성 패널", + "Remove": "제거", + "Resize": "크기 조정", + "Save Selected as Template": "선택 항목을 템플릿으로 저장", + "Search": "검색", + "Shapes": "형태", + "Title": "제목", + "Unpin": "고정 해제" + }, "dataTypes": { "AUDIO": "오디오", "BOOLEAN": "논리값", diff --git a/src/locales/ru/main.json b/src/locales/ru/main.json index a77bdb062..05c979f92 100644 --- a/src/locales/ru/main.json +++ b/src/locales/ru/main.json @@ -8,6 +8,39 @@ "red": "Красный", "yellow": "Жёлтый" }, + "contextMenu": { + " to input": " во вход", + " to widget": " в виджет", + "Add Group": "Добавить группу", + "Add Group For Selected Nodes": "Добавить группу для выбранных узлов", + "Add Node": "Добавить узел", + "Bypass": "Обход", + "Clone": "Клонировать", + "Collapse": "Свернуть", + "Colors": "Цвета", + "Convert ": "Преобразовать ", + "Convert Input to Widget": "Преобразовать вход в виджет", + "Convert Widget to Input": "Преобразовать виджет во вход", + "Convert to Group Node": "Преобразовать в групповой узел", + "Copy (Clipspace)": "Копировать (Clipspace)", + "Expand": "Развернуть", + "Inputs": "Входы", + "Manage": "Управлять", + "Manage Group Nodes": "Управление групповыми узлами", + "Mode": "Режим", + "Node Templates": "Шаблоны узлов", + "Outputs": "Выходы", + "Pin": "Закрепить", + "Properties": "Свойства", + "Properties Panel": "Панель свойств", + "Remove": "Удалить", + "Resize": "Изменить размер", + "Save Selected as Template": "Сохранить выбранное как шаблон", + "Search": "Поиск", + "Shapes": "Формы", + "Title": "Заголовок", + "Unpin": "Открепить" + }, "dataTypes": { "AUDIO": "АУДИО", "BOOLEAN": "БУЛЕВО", diff --git a/src/locales/zh/main.json b/src/locales/zh/main.json index 9e6a5f8db..cc2171bf2 100644 --- a/src/locales/zh/main.json +++ b/src/locales/zh/main.json @@ -8,6 +8,40 @@ "red": "红色", "yellow": "黄色" }, + "contextMenu": { + "zoomOut": "缩小", + "Inputs": "输入", + "Outputs": "输出", + "Properties": "属性", + "Properties Panel": "属性面板", + "Title": "标题", + "Mode": "模式", + "Resize": "调整大小", + "Collapse": "折叠", + "Expand": "展开", + "Pin": "固定", + "Unpin": "取消固定", + "Clone": "克隆", + "Remove": "删除", + "Colors": "颜色", + "Shapes": "形状", + "Bypass": "绕过", + "Copy (Clipspace)": "复制 (Clipspace)", + "Convert Widget to Input": "将控件转换为输入", + "Convert Input to Widget": "将输入转换为控件", + "Add Node": "添加节点", + "Add Group": "添加组", + "Convert to Group Node": "转换为组节点", + "Manage Group Nodes": "管理组节点", + "Add Group For Selected Nodes": "为选定节点添加组", + "Save Selected as Template": "将选定节点另存为模板", + "Node Templates": "节点模板", + "Manage": "管理", + "Convert ": "转换 ", + " to input": " 为输入", + " to widget": " 为控件", + "Search": "搜索" + }, "dataTypes": { "AUDIO": "音频", "BOOLEAN": "布尔",