mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-02 03:55:55 +00:00
update icon i18n
This commit is contained in:
3791
plans/translation-analysis-report.txt
Normal file
3791
plans/translation-analysis-report.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -24,7 +24,7 @@ class CallPipelineNode(WorkflowNode):
|
||||
|
||||
type_name = 'call_pipeline'
|
||||
category = 'action'
|
||||
icon = '⚙️'
|
||||
icon = 'Workflow'
|
||||
name = 'call_pipeline'
|
||||
description = 'call_pipeline'
|
||||
name_zh = '调用 Pipeline'
|
||||
|
||||
@@ -19,7 +19,7 @@ class CodeExecutorNode(WorkflowNode):
|
||||
|
||||
type_name = 'code_executor'
|
||||
category = 'process'
|
||||
icon = '💻'
|
||||
icon = 'Code'
|
||||
name = 'code_executor'
|
||||
description = 'code_executor'
|
||||
name_zh = '代码执行'
|
||||
|
||||
@@ -18,7 +18,7 @@ class ConditionNode(WorkflowNode):
|
||||
|
||||
type_name = 'condition'
|
||||
category = 'control'
|
||||
icon = '🔀'
|
||||
icon = 'GitBranch'
|
||||
name = 'condition'
|
||||
description = 'condition'
|
||||
name_zh = '条件分支'
|
||||
|
||||
@@ -17,7 +17,7 @@ class CronTriggerNode(WorkflowNode):
|
||||
|
||||
type_name = 'cron_trigger'
|
||||
category = 'trigger'
|
||||
icon = '⏰'
|
||||
icon = 'Timer'
|
||||
name = 'cron_trigger'
|
||||
description = 'cron_trigger'
|
||||
name_zh = '定时触发'
|
||||
|
||||
@@ -18,7 +18,7 @@ class DataTransformNode(WorkflowNode):
|
||||
|
||||
type_name = 'data_transform'
|
||||
category = 'process'
|
||||
icon = '🔄'
|
||||
icon = 'ArrowRightLeft'
|
||||
name = 'data_transform'
|
||||
description = 'data_transform'
|
||||
name_zh = '数据转换'
|
||||
|
||||
@@ -17,7 +17,7 @@ class EndNode(WorkflowNode):
|
||||
|
||||
type_name = 'end'
|
||||
category = 'action'
|
||||
icon = '🏁'
|
||||
icon = 'PauseCircle'
|
||||
name = 'end'
|
||||
description = 'end'
|
||||
name_zh = '结束'
|
||||
|
||||
@@ -17,7 +17,7 @@ class EventTriggerNode(WorkflowNode):
|
||||
|
||||
type_name = 'event_trigger'
|
||||
category = 'trigger'
|
||||
icon = '📡'
|
||||
icon = 'Bell'
|
||||
name = 'event_trigger'
|
||||
description = 'event_trigger'
|
||||
name_zh = '事件触发'
|
||||
|
||||
@@ -17,7 +17,7 @@ class HTTPRequestNode(WorkflowNode):
|
||||
|
||||
type_name = 'http_request'
|
||||
category = 'process'
|
||||
icon = '🌐'
|
||||
icon = 'Globe'
|
||||
name = 'http_request'
|
||||
description = 'http_request'
|
||||
name_zh = 'HTTP 请求'
|
||||
|
||||
@@ -14,7 +14,7 @@ class IteratorNode(WorkflowNode):
|
||||
|
||||
type_name = 'iterator'
|
||||
category = 'control'
|
||||
icon = '🔄'
|
||||
icon = 'Repeat'
|
||||
name = 'iterator'
|
||||
name_zh = '迭代器'
|
||||
name_en = 'Iterator'
|
||||
|
||||
@@ -17,7 +17,7 @@ class KnowledgeRetrievalNode(WorkflowNode):
|
||||
|
||||
type_name = 'knowledge_retrieval'
|
||||
category = 'process'
|
||||
icon = '📚'
|
||||
icon = 'Search'
|
||||
name = 'knowledge_retrieval'
|
||||
description = 'knowledge_retrieval'
|
||||
name_zh = '知识库检索'
|
||||
|
||||
@@ -20,7 +20,7 @@ class LLMCallNode(WorkflowNode):
|
||||
|
||||
type_name = 'llm_call'
|
||||
category = 'process'
|
||||
icon = '🤖'
|
||||
icon = 'Brain'
|
||||
name = 'llm_call'
|
||||
name_zh = 'LLM 调用'
|
||||
name_en = 'LLM Call'
|
||||
|
||||
@@ -14,7 +14,7 @@ class LoopNode(WorkflowNode):
|
||||
|
||||
type_name = 'loop'
|
||||
category = 'control'
|
||||
icon = '🔁'
|
||||
icon = 'Repeat'
|
||||
name = 'loop'
|
||||
name_zh = '循环'
|
||||
name_en = 'Loop'
|
||||
|
||||
@@ -17,7 +17,7 @@ class MergeNode(WorkflowNode):
|
||||
|
||||
type_name = 'merge'
|
||||
category = 'control'
|
||||
icon = '🔗'
|
||||
icon = 'GitMerge'
|
||||
name = 'merge'
|
||||
description = 'merge'
|
||||
name_zh = '合并'
|
||||
|
||||
@@ -19,7 +19,7 @@ class MessageTriggerNode(WorkflowNode):
|
||||
|
||||
type_name = 'message_trigger'
|
||||
category = 'trigger'
|
||||
icon = '💬'
|
||||
icon = 'MessageSquare'
|
||||
name = 'message_trigger'
|
||||
description = 'message_trigger'
|
||||
name_zh = '消息触发'
|
||||
|
||||
@@ -17,7 +17,7 @@ class OpeningStatementNode(WorkflowNode):
|
||||
|
||||
type_name = 'opening_statement'
|
||||
category = 'action'
|
||||
icon = '👋'
|
||||
icon = 'MessageSquare'
|
||||
name = 'opening_statement'
|
||||
description = 'opening_statement'
|
||||
name_zh = '对话开场白'
|
||||
|
||||
@@ -14,7 +14,7 @@ class ParallelNode(WorkflowNode):
|
||||
|
||||
type_name = 'parallel'
|
||||
category = 'control'
|
||||
icon = '⚡'
|
||||
icon = 'Layers'
|
||||
name = 'parallel'
|
||||
name_zh = '并行执行'
|
||||
name_en = 'Parallel'
|
||||
|
||||
@@ -17,7 +17,7 @@ class ParameterExtractorNode(WorkflowNode):
|
||||
|
||||
type_name = 'parameter_extractor'
|
||||
category = 'process'
|
||||
icon: str = '📤'
|
||||
icon: str = 'Variable'
|
||||
name = 'parameter_extractor'
|
||||
description = 'parameter_extractor'
|
||||
name_zh = '参数提取器'
|
||||
|
||||
@@ -17,7 +17,7 @@ class QuestionClassifierNode(WorkflowNode):
|
||||
|
||||
type_name = 'question_classifier'
|
||||
category = 'process'
|
||||
icon = '🏷️'
|
||||
icon = 'ListFilter'
|
||||
name = 'question_classifier'
|
||||
description = 'question_classifier'
|
||||
name_zh = '问题分类器'
|
||||
|
||||
@@ -20,7 +20,7 @@ class ReplyMessageNode(WorkflowNode):
|
||||
|
||||
type_name = 'reply_message'
|
||||
category = 'action'
|
||||
icon = '↩️'
|
||||
icon = 'Send'
|
||||
name = 'reply_message'
|
||||
description = 'reply_message'
|
||||
name_zh = '回复消息'
|
||||
|
||||
@@ -17,7 +17,7 @@ class SendMessageNode(WorkflowNode):
|
||||
|
||||
type_name = 'send_message'
|
||||
category = 'action'
|
||||
icon = '📤'
|
||||
icon = 'MessageCircle'
|
||||
name = 'send_message'
|
||||
description = 'send_message'
|
||||
name_zh = '发送消息'
|
||||
|
||||
@@ -17,7 +17,7 @@ class SetVariableNode(WorkflowNode):
|
||||
|
||||
type_name = 'set_variable'
|
||||
category = 'action'
|
||||
icon = '📝'
|
||||
icon = 'Variable'
|
||||
name = 'set_variable'
|
||||
description = 'set_variable'
|
||||
name_zh = '设置变量'
|
||||
|
||||
@@ -17,7 +17,7 @@ class StoreDataNode(WorkflowNode):
|
||||
|
||||
type_name = 'store_data'
|
||||
category = 'action'
|
||||
icon = '💾'
|
||||
icon = 'Database'
|
||||
name = 'store_data'
|
||||
description = 'store_data'
|
||||
name_zh = '存储数据'
|
||||
|
||||
@@ -17,7 +17,7 @@ class SwitchNode(WorkflowNode):
|
||||
|
||||
type_name = 'switch'
|
||||
category = 'control'
|
||||
icon = '🔃'
|
||||
icon = 'Split'
|
||||
name = 'switch'
|
||||
description = 'switch'
|
||||
name_zh = '多路分支'
|
||||
|
||||
@@ -17,7 +17,7 @@ class VariableAggregatorNode(WorkflowNode):
|
||||
|
||||
type_name = 'variable_aggregator'
|
||||
category = 'control'
|
||||
icon = '📊'
|
||||
icon = 'GitMerge'
|
||||
name = 'variable_aggregator'
|
||||
description = 'variable_aggregator'
|
||||
name_zh = '变量聚合器'
|
||||
|
||||
@@ -17,7 +17,7 @@ class WaitNode(WorkflowNode):
|
||||
|
||||
type_name = 'wait'
|
||||
category = 'control'
|
||||
icon = '⏳'
|
||||
icon = 'Clock'
|
||||
name = 'wait'
|
||||
description = 'wait'
|
||||
name_zh = '等待'
|
||||
|
||||
@@ -17,7 +17,7 @@ class WebhookTriggerNode(WorkflowNode):
|
||||
|
||||
type_name = 'webhook_trigger'
|
||||
category = 'trigger'
|
||||
icon = '🌐'
|
||||
icon = 'Webhook'
|
||||
name = 'webhook_trigger'
|
||||
description = 'webhook_trigger'
|
||||
name_zh = 'Webhook 触发'
|
||||
|
||||
151
web/ai_translate_workflows.py
Normal file
151
web/ai_translate_workflows.py
Normal file
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
AI翻译工作流i18n - 使用完整短语翻译而非词汇替换
|
||||
"""
|
||||
|
||||
import json
|
||||
import re
|
||||
from typing import Dict, List
|
||||
|
||||
def has_chinese(text: str) -> bool:
|
||||
"""检查文本是否包含中文字符"""
|
||||
return bool(re.search(r'[\u4e00-\u9fff]', text))
|
||||
|
||||
def load_translations() -> Dict:
|
||||
"""加载翻译文件"""
|
||||
with open('workflows_translations.json', 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
|
||||
def save_translations(data: Dict):
|
||||
"""保存翻译文件"""
|
||||
with open('workflows_translations.json', 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||
|
||||
# 完整短语翻译字典 - 第一批(基础UI和操作)
|
||||
TRANSLATIONS_BATCH_1 = {
|
||||
# 基础操作
|
||||
"工作流": {"es-ES": "Flujo de trabajo", "ru-RU": "Рабочий процесс", "th-TH": "เวิร์กโฟลว์", "vi-VN": "Quy trình làm việc"},
|
||||
"节点": {"es-ES": "Nodo", "ru-RU": "Узел", "th-TH": "โหนด", "vi-VN": "Nút"},
|
||||
"创建工作流": {"es-ES": "Crear flujo de trabajo", "ru-RU": "Создать рабочий процесс", "th-TH": "สร้างเวิร์กโฟลว์", "vi-VN": "Tạo quy trình làm việc"},
|
||||
"从侧边栏选择": {"es-ES": "Seleccionar desde la barra lateral", "ru-RU": "Выбрать из боковой панели", "th-TH": "เลือกจากแถบด้านข้าง", "vi-VN": "Chọn từ thanh bên"},
|
||||
"编辑工作流": {"es-ES": "Editar flujo de trabajo", "ru-RU": "Редактировать рабочий процесс", "th-TH": "แก้ไขเวิร์กโฟลว์", "vi-VN": "Chỉnh sửa quy trình làm việc"},
|
||||
"新建工作流": {"es-ES": "Nuevo flujo de trabajo", "ru-RU": "Новый рабочий процесс", "th-TH": "เวิร์กโฟลว์ใหม่", "vi-VN": "Quy trình làm việc mới"},
|
||||
"获取工作流列表失败": {"es-ES": "Error al obtener la lista de flujos de trabajo", "ru-RU": "Не удалось получить список рабочих процессов", "th-TH": "ไม่สามารถรับรายการเวิร์กโฟลว์", "vi-VN": "Không thể lấy danh sách quy trình làm việc"},
|
||||
"工作流名称": {"es-ES": "Nombre del flujo de trabajo", "ru-RU": "Название рабочего процесса", "th-TH": "ชื่อเวิร์กโฟลว์", "vi-VN": "Tên quy trình làm việc"},
|
||||
"工作流描述": {"es-ES": "Descripción del flujo de trabajo", "ru-RU": "Описание рабочего процесса", "th-TH": "คำอธิบายเวิร์กโฟลว์", "vi-VN": "Mô tả quy trình làm việc"},
|
||||
"工作流名称不能为空": {"es-ES": "El nombre del flujo de trabajo no puede estar vacío", "ru-RU": "Название рабочего процесса не может быть пустым", "th-TH": "ชื่อเวิร์กโฟลว์ไม่สามารถว่างเปล่า", "vi-VN": "Tên quy trình làm việc không được để trống"},
|
||||
"默认工作流描述": {"es-ES": "Descripción predeterminada del flujo de trabajo", "ru-RU": "Описание рабочего процесса по умолчанию", "th-TH": "คำอธิบายเวิร์กโฟลว์เริ่มต้น", "vi-VN": "Mô tả quy trình làm việc mặc định"},
|
||||
|
||||
# 成功/失败消息
|
||||
"保存成功": {"es-ES": "Guardado exitosamente", "ru-RU": "Успешно сохранено", "th-TH": "บันทึกสำเร็จ", "vi-VN": "Lưu thành công"},
|
||||
"保存失败": {"es-ES": "Error al guardar", "ru-RU": "Не удалось сохранить", "th-TH": "บันทึกล้มเหลว", "vi-VN": "Lưu thất bại"},
|
||||
"创建成功": {"es-ES": "Creado exitosamente", "ru-RU": "Успешно создано", "th-TH": "สร้างสำเร็จ", "vi-VN": "Tạo thành công"},
|
||||
"创建失败": {"es-ES": "Error al crear", "ru-RU": "Не удалось создать", "th-TH": "สร้างล้มเหลว", "vi-VN": "Tạo thất bại"},
|
||||
"删除成功": {"es-ES": "Eliminado exitosamente", "ru-RU": "Успешно удалено", "th-TH": "ลบสำเร็จ", "vi-VN": "Xóa thành công"},
|
||||
"删除失败": {"es-ES": "Error al eliminar", "ru-RU": "Не удалось удалить", "th-TH": "ลบล้มเหลว", "vi-VN": "Xóa thất bại"},
|
||||
"复制成功": {"es-ES": "Copiado exitosamente", "ru-RU": "Успешно скопировано", "th-TH": "คัดลอกสำเร็จ", "vi-VN": "Sao chép thành công"},
|
||||
"复制失败": {"es-ES": "Error al copiar", "ru-RU": "Не удалось скопировать", "th-TH": "คัดลอกล้มเหลว", "vi-VN": "Sao chép thất bại"},
|
||||
"导出成功": {"es-ES": "Exportado exitosamente", "ru-RU": "Успешно экспортировано", "th-TH": "ส่งออกสำเร็จ", "vi-VN": "Xuất thành công"},
|
||||
"导入成功": {"es-ES": "Importado exitosamente", "ru-RU": "Успешно импортировано", "th-TH": "นำเข้าสำเร็จ", "vi-VN": "Nhập thành công"},
|
||||
"导入失败": {"es-ES": "Error al importar", "ru-RU": "Не удалось импортировать", "th-TH": "นำเข้าล้มเหลว", "vi-VN": "Nhập thất bại"},
|
||||
"发布成功": {"es-ES": "Publicado exitosamente", "ru-RU": "Успешно опубликовано", "th-TH": "เผยแพร่สำเร็จ", "vi-VN": "Xuất bản thành công"},
|
||||
"发布失败": {"es-ES": "Error al publicar", "ru-RU": "Не удалось опубликовать", "th-TH": "เผยแพร่ล้มเหลว", "vi-VN": "Xuất bản thất bại"},
|
||||
"获取工作流失败": {"es-ES": "Error al obtener el flujo de trabajo", "ru-RU": "Не удалось получить рабочий процесс", "th-TH": "ไม่สามารถรับเวิร์กโฟลว์", "vi-VN": "Không thể lấy quy trình làm việc"},
|
||||
"加载失败": {"es-ES": "Error al cargar", "ru-RU": "Не удалось загрузить", "th-TH": "โหลดล้มเหลว", "vi-VN": "Tải thất bại"},
|
||||
|
||||
# 确认对话框
|
||||
"确认删除": {"es-ES": "Confirmar eliminación", "ru-RU": "Подтвердить удаление", "th-TH": "ยืนยันการลบ", "vi-VN": "Xác nhận xóa"},
|
||||
"确认删除此工作流吗?": {"es-ES": "¿Confirmar la eliminación de este flujo de trabajo?", "ru-RU": "Подтвердить удаление этого рабочего процесса?", "th-TH": "ยืนยันการลบเวิร์กโฟลว์นี้หรือไม่?", "vi-VN": "Xác nhận xóa quy trình làm việc này?"},
|
||||
|
||||
# 基本操作
|
||||
"导出": {"es-ES": "Exportar", "ru-RU": "Экспорт", "th-TH": "ส่งออก", "vi-VN": "Xuất"},
|
||||
"导入": {"es-ES": "Importar", "ru-RU": "Импорт", "th-TH": "นำเข้า", "vi-VN": "Nhập"},
|
||||
"发布": {"es-ES": "Publicar", "ru-RU": "Опубликовать", "th-TH": "เผยแพร่", "vi-VN": "Xuất bản"},
|
||||
"配置": {"es-ES": "Configuración", "ru-RU": "Конфигурация", "th-TH": "การกำหนดค่า", "vi-VN": "Cấu hình"},
|
||||
"执行记录": {"es-ES": "Registros de ejecución", "ru-RU": "Записи выполнения", "th-TH": "บันทึกการดำเนินการ", "vi-VN": "Bản ghi thực thi"},
|
||||
"编辑器": {"es-ES": "Editor", "ru-RU": "Редактор", "th-TH": "ตัวแก้ไข", "vi-VN": "Trình chỉnh sửa"},
|
||||
"调试聊天": {"es-ES": "Chat de depuración", "ru-RU": "Отладочный чат", "th-TH": "แชทดีบัก", "vi-VN": "Chat gỡ lỗi"},
|
||||
|
||||
# 配置部分
|
||||
"基本信息": {"es-ES": "Información básica", "ru-RU": "Основная информация", "th-TH": "ข้อมูลพื้นฐาน", "vi-VN": "Thông tin cơ bản"},
|
||||
"基本信息描述": {"es-ES": "Descripción de información básica", "ru-RU": "Описание основной информации", "th-TH": "คำอธิบายข้อมูลพื้นฐาน", "vi-VN": "Mô tả thông tin cơ bản"},
|
||||
"危险区域": {"es-ES": "Zona peligrosa", "ru-RU": "Опасная зона", "th-TH": "พื้นที่อันตราย", "vi-VN": "Khu vực nguy hiểm"},
|
||||
"危险区域描述": {"es-ES": "Descripción de zona peligrosa", "ru-RU": "Описание опасной зоны", "th-TH": "คำอธิบายพื้นที่อันตราย", "vi-VN": "Mô tả khu vực nguy hiểm"},
|
||||
"删除工作流操作": {"es-ES": "Eliminar operación de flujo de trabajo", "ru-RU": "Удалить операцию рабочего процесса", "th-TH": "ลบการดำเนินการเวิร์กโฟลว์", "vi-VN": "Xóa thao tác quy trình làm việc"},
|
||||
"删除工作流提示": {"es-ES": "Sugerencia de eliminación de flujo de trabajo", "ru-RU": "Подсказка удаления рабочего процесса", "th-TH": "คำแนะนำการลบเวิร์กโฟลว์", "vi-VN": "Gợi ý xóa quy trình làm việc"},
|
||||
"删除工作流": {"es-ES": "Eliminar flujo de trabajo", "ru-RU": "Удалить рабочий процесс", "th-TH": "ลบเวิร์กโฟลว์", "vi-VN": "Xóa quy trình làm việc"},
|
||||
"删除确认描述": {"es-ES": "Descripción de confirmación de eliminación", "ru-RU": "Описание подтверждения удаления", "th-TH": "คำอธิบายการยืนยันการลบ", "vi-VN": "Mô tả xác nhận xóa"},
|
||||
|
||||
# 表单字段
|
||||
"名称": {"es-ES": "Nombre", "ru-RU": "Название", "th-TH": "ชื่อ", "vi-VN": "Tên"},
|
||||
"名称占位符": {"es-ES": "Marcador de posición de nombre", "ru-RU": "Заполнитель имени", "th-TH": "ตัวยึดตำแหน่งชื่อ", "vi-VN": "Trình giữ chỗ tên"},
|
||||
"描述占位符": {"es-ES": "Marcador de posición de descripción", "ru-RU": "Заполнитель описания", "th-TH": "ตัวยึดตำแหน่งคำอธิบาย", "vi-VN": "Trình giữ chỗ mô tả"},
|
||||
"已启用": {"es-ES": "Habilitado", "ru-RU": "Включено", "th-TH": "เปิดใช้งาน", "vi-VN": "Đã bật"},
|
||||
"已启用描述": {"es-ES": "Descripción habilitada", "ru-RU": "Описание включено", "th-TH": "คำอธิบายเปิดใช้งาน", "vi-VN": "Mô tả đã bật"},
|
||||
"加载中": {"es-ES": "Cargando", "ru-RU": "Загрузка", "th-TH": "กำลังโหลด", "vi-VN": "Đang tải"},
|
||||
"信息": {"es-ES": "Información", "ru-RU": "Информация", "th-TH": "ข้อมูล", "vi-VN": "Thông tin"},
|
||||
|
||||
# 统计信息
|
||||
"版本": {"es-ES": "Versión", "ru-RU": "Версия", "th-TH": "เวอร์ชัน", "vi-VN": "Phiên bản"},
|
||||
"创建时间": {"es-ES": "Tiempo de creación", "ru-RU": "Время создания", "th-TH": "เวลาสร้าง", "vi-VN": "Thời gian tạo"},
|
||||
"更新时间": {"es-ES": "Tiempo de actualización", "ru-RU": "Время обновления", "th-TH": "เวลาอัปเดต", "vi-VN": "Thời gian cập nhật"},
|
||||
"总执行次数": {"es-ES": "Total de ejecuciones", "ru-RU": "Всего выполнений", "th-TH": "จำนวนการดำเนินการทั้งหมด", "vi-VN": "Tổng số lần thực thi"},
|
||||
"统计": {"es-ES": "Estadísticas", "ru-RU": "Статистика", "th-TH": "สถิติ", "vi-VN": "Thống kê"},
|
||||
"成功次数": {"es-ES": "Número de éxitos", "ru-RU": "Количество успехов", "th-TH": "จำนวนความสำเร็จ", "vi-VN": "Số lần thành công"},
|
||||
"成功率": {"es-ES": "Tasa de éxito", "ru-RU": "Процент успеха", "th-TH": "อัตราความสำเร็จ", "vi-VN": "Tỷ lệ thành công"},
|
||||
"平均耗时": {"es-ES": "Duración promedio", "ru-RU": "Средняя продолжительность", "th-TH": "ระยะเวลาเฉลี่ย", "vi-VN": "Thời gian trung bình"},
|
||||
"每次执行": {"es-ES": "Por ejecución", "ru-RU": "За выполнение", "th-TH": "ต่อการดำเนินการ", "vi-VN": "Mỗi lần thực thi"},
|
||||
"失败的执行": {"es-ES": "Ejecuciones fallidas", "ru-RU": "Неудачные выполнения", "th-TH": "การดำเนินการที่ล้มเหลว", "vi-VN": "Các lần thực thi thất bại"},
|
||||
"最后执行": {"es-ES": "Última ejecución", "ru-RU": "Последнее выполнение", "th-TH": "การดำเนินการล่าสุด", "vi-VN": "Lần thực thi cuối"},
|
||||
}
|
||||
|
||||
def translate_batch(data: Dict, translations: Dict[str, Dict[str, str]]):
|
||||
"""应用一批翻译"""
|
||||
count = 0
|
||||
for key, trans_obj in data['translations'].items():
|
||||
zh_text = trans_obj.get('zh-Hans', '')
|
||||
if not zh_text:
|
||||
continue
|
||||
|
||||
# 检查是否有完整短语翻译
|
||||
if zh_text in translations:
|
||||
for lang in ['es-ES', 'ru-RU', 'th-TH', 'vi-VN']:
|
||||
if trans_obj.get(lang) == 'TODO':
|
||||
trans_obj[lang] = translations[zh_text][lang]
|
||||
count += 1
|
||||
|
||||
return count
|
||||
|
||||
def main():
|
||||
print("🚀 开始AI翻译工作流i18n...")
|
||||
|
||||
# 加载数据
|
||||
data = load_translations()
|
||||
|
||||
# 应用第一批翻译
|
||||
print("\n📝 应用第一批翻译(基础UI和操作)...")
|
||||
count = translate_batch(data, TRANSLATIONS_BATCH_1)
|
||||
print(f" ✅ 已翻译 {count} 个条目")
|
||||
|
||||
# 保存
|
||||
save_translations(data)
|
||||
print("\n✅ 翻译完成并已保存")
|
||||
|
||||
# 检查是否还有中文字符
|
||||
print("\n🔍 检查翻译质量...")
|
||||
problem_count = 0
|
||||
for key, trans_obj in data['translations'].items():
|
||||
for lang in ['es-ES', 'ru-RU', 'th-TH', 'vi-VN']:
|
||||
text = trans_obj.get(lang, '')
|
||||
if text != 'TODO' and has_chinese(text):
|
||||
problem_count += 1
|
||||
print(f" ⚠️ {key} ({lang}): {text}")
|
||||
|
||||
if problem_count == 0:
|
||||
print(" ✅ 所有翻译都不包含中文字符")
|
||||
else:
|
||||
print(f" ⚠️ 发现 {problem_count} 个翻译仍包含中文字符")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
141
web/analyze_missing_translations.py
Normal file
141
web/analyze_missing_translations.py
Normal file
@@ -0,0 +1,141 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
分析所有语言文件中缺失的翻译键
|
||||
对比 zh-Hans.ts(完整翻译)和其他语言文件,找出所有英文键
|
||||
"""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Set
|
||||
|
||||
def extract_keys_from_ts(file_path: Path) -> Dict[str, str]:
|
||||
"""从 TypeScript 文件中提取所有键值对"""
|
||||
content = file_path.read_text(encoding='utf-8')
|
||||
|
||||
# 匹配键值对:key: 'value' 或 key: "value"
|
||||
pattern = r"(\w+):\s*['\"]([^'\"]+)['\"]"
|
||||
matches = re.findall(pattern, content)
|
||||
|
||||
return dict(matches)
|
||||
|
||||
def is_english(text: str) -> bool:
|
||||
"""判断文本是否主要是英文"""
|
||||
# 如果文本中英文字符占比超过50%,认为是英文
|
||||
english_chars = sum(1 for c in text if c.isascii() and c.isalpha())
|
||||
total_chars = sum(1 for c in text if c.isalpha())
|
||||
|
||||
if total_chars == 0:
|
||||
return False
|
||||
|
||||
return english_chars / total_chars > 0.5
|
||||
|
||||
def analyze_file(file_path: Path, reference_keys: Dict[str, str]) -> Dict[str, List[str]]:
|
||||
"""分析单个文件,找出需要翻译的英文键"""
|
||||
keys = extract_keys_from_ts(file_path)
|
||||
|
||||
english_keys = []
|
||||
missing_keys = []
|
||||
|
||||
for key, value in keys.items():
|
||||
if is_english(value):
|
||||
english_keys.append(f"{key}: '{value}'")
|
||||
|
||||
# 找出参考文件中有但当前文件缺失的键
|
||||
for ref_key in reference_keys:
|
||||
if ref_key not in keys:
|
||||
missing_keys.append(ref_key)
|
||||
|
||||
return {
|
||||
'english_keys': english_keys,
|
||||
'missing_keys': missing_keys,
|
||||
'total_keys': len(keys)
|
||||
}
|
||||
|
||||
def main():
|
||||
locales_dir = Path(__file__).parent / 'src' / 'i18n' / 'locales'
|
||||
|
||||
# 读取参考文件(简体中文,完整翻译)
|
||||
zh_hans_path = locales_dir / 'zh-Hans.ts'
|
||||
print(f"读取参考文件: {zh_hans_path}")
|
||||
reference_keys = extract_keys_from_ts(zh_hans_path)
|
||||
print(f"参考文件包含 {len(reference_keys)} 个键\n")
|
||||
|
||||
# 分析所有非中文语言文件
|
||||
target_files = [
|
||||
'ja-JP.ts',
|
||||
'zh-Hant.ts',
|
||||
'es-ES.ts',
|
||||
'ru-RU.ts',
|
||||
'th-TH.ts',
|
||||
'vi-VN.ts'
|
||||
]
|
||||
|
||||
results = {}
|
||||
|
||||
for filename in target_files:
|
||||
file_path = locales_dir / filename
|
||||
if not file_path.exists():
|
||||
print(f"⚠️ 文件不存在: {filename}")
|
||||
continue
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print(f"分析文件: {filename}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
result = analyze_file(file_path, reference_keys)
|
||||
results[filename] = result
|
||||
|
||||
print(f"总键数: {result['total_keys']}")
|
||||
print(f"英文键数: {len(result['english_keys'])}")
|
||||
print(f"缺失键数: {len(result['missing_keys'])}")
|
||||
|
||||
if result['english_keys']:
|
||||
print(f"\n前10个英文键示例:")
|
||||
for key in result['english_keys'][:10]:
|
||||
print(f" - {key}")
|
||||
|
||||
if result['missing_keys']:
|
||||
print(f"\n前10个缺失键示例:")
|
||||
for key in result['missing_keys'][:10]:
|
||||
print(f" - {key}")
|
||||
|
||||
# 汇总统计
|
||||
print(f"\n\n{'='*60}")
|
||||
print("汇总统计")
|
||||
print(f"{'='*60}")
|
||||
|
||||
total_english = sum(len(r['english_keys']) for r in results.values())
|
||||
total_missing = sum(len(r['missing_keys']) for r in results.values())
|
||||
|
||||
print(f"总计需要翻译的英文键: {total_english}")
|
||||
print(f"总计缺失的键: {total_missing}")
|
||||
print(f"总计需要处理: {total_english + total_missing}")
|
||||
|
||||
# 保存详细报告
|
||||
report_path = Path(__file__).parent.parent / 'plans' / 'translation-analysis-report.txt'
|
||||
with open(report_path, 'w', encoding='utf-8') as f:
|
||||
f.write("翻译分析报告\n")
|
||||
f.write("="*60 + "\n\n")
|
||||
|
||||
for filename, result in results.items():
|
||||
f.write(f"\n文件: {filename}\n")
|
||||
f.write(f"总键数: {result['total_keys']}\n")
|
||||
f.write(f"英文键数: {len(result['english_keys'])}\n")
|
||||
f.write(f"缺失键数: {len(result['missing_keys'])}\n")
|
||||
|
||||
if result['english_keys']:
|
||||
f.write(f"\n英文键列表:\n")
|
||||
for key in result['english_keys']:
|
||||
f.write(f" {key}\n")
|
||||
|
||||
if result['missing_keys']:
|
||||
f.write(f"\n缺失键列表:\n")
|
||||
for key in result['missing_keys']:
|
||||
f.write(f" {key}\n")
|
||||
|
||||
f.write("\n" + "="*60 + "\n")
|
||||
|
||||
print(f"\n详细报告已保存到: {report_path}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
262
web/apply_workflows_translations.py
Normal file
262
web/apply_workflows_translations.py
Normal file
@@ -0,0 +1,262 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
应用workflows翻译到语言文件
|
||||
|
||||
使用方法:
|
||||
1. 编辑 workflows_translations.json 文件
|
||||
2. 将 'TODO' 替换为实际翻译
|
||||
3. 运行此脚本:python3 apply_workflows_translations.py
|
||||
|
||||
脚本会:
|
||||
- 读取 workflows_translations.json
|
||||
- 识别已完成的翻译(非"TODO")
|
||||
- 应用到对应的语言文件
|
||||
- 生成进度报告
|
||||
- 支持增量应用(可以分批翻译)
|
||||
"""
|
||||
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Tuple, Set
|
||||
|
||||
|
||||
def load_translations() -> Dict:
|
||||
"""加载翻译JSON文件"""
|
||||
json_path = Path(__file__).parent / 'workflows_translations.json'
|
||||
|
||||
if not json_path.exists():
|
||||
raise FileNotFoundError(f"找不到翻译文件: {json_path}")
|
||||
|
||||
with open(json_path, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def get_completed_translations(translations_data: Dict) -> Dict[str, Dict[str, str]]:
|
||||
"""
|
||||
获取已完成的翻译
|
||||
|
||||
返回格式:
|
||||
{
|
||||
'ja-JP': {'title': '工作流对话', 'description': '...', ...},
|
||||
'zh-Hant': {...},
|
||||
...
|
||||
}
|
||||
"""
|
||||
translations = translations_data.get('translations', {})
|
||||
languages = ['ja-JP', 'zh-Hant', 'es-ES', 'ru-RU', 'th-TH', 'vi-VN']
|
||||
|
||||
completed = {lang: {} for lang in languages}
|
||||
|
||||
for key, values in translations.items():
|
||||
for lang in languages:
|
||||
translation = values.get(lang, 'TODO')
|
||||
if translation != 'TODO' and translation.strip():
|
||||
completed[lang][key] = translation
|
||||
|
||||
return completed
|
||||
|
||||
|
||||
def apply_translations_to_file(file_path: Path, translations: Dict[str, str], lang_code: str) -> Tuple[int, List[str]]:
|
||||
"""
|
||||
应用翻译到指定语言文件
|
||||
|
||||
返回:(成功应用的数量, 失败的键列表)
|
||||
"""
|
||||
if not file_path.exists():
|
||||
print(f"⚠️ 文件不存在: {file_path}")
|
||||
return 0, list(translations.keys())
|
||||
|
||||
# 读取文件内容
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# 找到workflows部分的起始和结束位置
|
||||
workflows_start = content.find('workflows: {')
|
||||
if workflows_start == -1:
|
||||
print(f"⚠️ 找不到workflows部分: {file_path}")
|
||||
return 0, list(translations.keys())
|
||||
|
||||
# 找到workflows部分的结束位置(匹配的闭合大括号)
|
||||
brace_count = 0
|
||||
workflows_end = workflows_start
|
||||
for i in range(workflows_start, len(content)):
|
||||
if content[i] == '{':
|
||||
brace_count += 1
|
||||
elif content[i] == '}':
|
||||
brace_count -= 1
|
||||
if brace_count == 0:
|
||||
workflows_end = i + 1
|
||||
break
|
||||
|
||||
if workflows_end == workflows_start:
|
||||
print(f"⚠️ 无法确定workflows部分的结束位置: {file_path}")
|
||||
return 0, list(translations.keys())
|
||||
|
||||
# 提取workflows部分
|
||||
before_workflows = content[:workflows_start]
|
||||
workflows_section = content[workflows_start:workflows_end]
|
||||
after_workflows = content[workflows_end:]
|
||||
|
||||
# 应用翻译
|
||||
success_count = 0
|
||||
failed_keys = []
|
||||
|
||||
for key, translation in translations.items():
|
||||
# 转义特殊字符
|
||||
escaped_key = re.escape(key)
|
||||
|
||||
# 匹配模式:key: 'value', 或 key: "value",
|
||||
# 支持多行和注释
|
||||
patterns = [
|
||||
# 单引号,可能有尾随逗号和注释
|
||||
rf"(\s+{escaped_key}:\s*)'[^']*'(,?\s*(?://.*)?(?:\n|$))",
|
||||
# 双引号,可能有尾随逗号和注释
|
||||
rf'(\s+{escaped_key}:\s*)"[^"]*"(,?\s*(?://.*)?(?:\n|$))',
|
||||
]
|
||||
|
||||
replaced = False
|
||||
for pattern in patterns:
|
||||
if re.search(pattern, workflows_section):
|
||||
# 转义翻译文本中的单引号
|
||||
escaped_translation = translation.replace("'", "\\'")
|
||||
replacement = rf"\1'{escaped_translation}'\2"
|
||||
workflows_section = re.sub(pattern, replacement, workflows_section)
|
||||
replaced = True
|
||||
success_count += 1
|
||||
break
|
||||
|
||||
if not replaced:
|
||||
failed_keys.append(key)
|
||||
|
||||
# 重新组合文件内容
|
||||
new_content = before_workflows + workflows_section + after_workflows
|
||||
|
||||
# 写回文件
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.write(new_content)
|
||||
|
||||
return success_count, failed_keys
|
||||
|
||||
|
||||
def update_progress_in_json(json_path: Path, completed_by_lang: Dict[str, int]):
|
||||
"""更新JSON文件中的进度信息"""
|
||||
with open(json_path, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
|
||||
# 计算总的已翻译键数(取所有语言的平均值)
|
||||
total_translated = sum(completed_by_lang.values()) // len(completed_by_lang) if completed_by_lang else 0
|
||||
total_keys = data.get('_progress', {}).get('total_keys', 0)
|
||||
|
||||
data['_progress']['translated_keys'] = total_translated
|
||||
data['_progress']['remaining_keys'] = total_keys - total_translated
|
||||
data['_progress']['by_language'] = completed_by_lang
|
||||
|
||||
with open(json_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
def generate_report(results: Dict[str, Tuple[int, List[str]]], total_keys: int):
|
||||
"""生成应用报告"""
|
||||
print("\n" + "="*80)
|
||||
print("📊 Workflows翻译应用报告")
|
||||
print("="*80)
|
||||
|
||||
total_applied = 0
|
||||
|
||||
for lang, (success_count, failed_keys) in results.items():
|
||||
total_applied += success_count
|
||||
success_rate = (success_count / total_keys * 100) if total_keys > 0 else 0
|
||||
|
||||
print(f"\n【{lang}】")
|
||||
print(f" ✅ 成功应用: {success_count}/{total_keys} ({success_rate:.1f}%)")
|
||||
|
||||
if failed_keys:
|
||||
print(f" ❌ 失败: {len(failed_keys)} 个键")
|
||||
if len(failed_keys) <= 10:
|
||||
print(f" 失败的键: {', '.join(failed_keys)}")
|
||||
else:
|
||||
print(f" 失败的键(前10个): {', '.join(failed_keys[:10])}...")
|
||||
|
||||
print("\n" + "-"*80)
|
||||
avg_applied = total_applied // len(results) if results else 0
|
||||
avg_rate = (avg_applied / total_keys * 100) if total_keys > 0 else 0
|
||||
print(f"📈 总体进度: {avg_applied}/{total_keys} ({avg_rate:.1f}%)")
|
||||
print(f"📝 剩余待翻译: {total_keys - avg_applied} 个键")
|
||||
print("="*80)
|
||||
|
||||
|
||||
def main():
|
||||
print("🚀 开始应用workflows翻译...")
|
||||
|
||||
# 加载翻译数据
|
||||
print("\n📖 读取翻译文件...")
|
||||
translations_data = load_translations()
|
||||
total_keys = translations_data.get('_progress', {}).get('total_keys', 0)
|
||||
print(f" 总键数: {total_keys}")
|
||||
|
||||
# 获取已完成的翻译
|
||||
print("\n🔍 识别已完成的翻译...")
|
||||
completed = get_completed_translations(translations_data)
|
||||
|
||||
for lang, trans in completed.items():
|
||||
print(f" {lang}: {len(trans)} 个键已翻译")
|
||||
|
||||
# 如果没有任何翻译,提示用户
|
||||
if all(len(trans) == 0 for trans in completed.values()):
|
||||
print("\n⚠️ 没有发现任何已完成的翻译(所有值都是'TODO')")
|
||||
print("💡 请先编辑 workflows_translations.json 文件,将 'TODO' 替换为实际翻译")
|
||||
return
|
||||
|
||||
# 应用翻译到各个语言文件
|
||||
print("\n✏️ 应用翻译到语言文件...")
|
||||
|
||||
locales_dir = Path(__file__).parent / 'src' / 'i18n' / 'locales'
|
||||
|
||||
language_files = {
|
||||
'ja-JP': locales_dir / 'ja-JP.ts',
|
||||
'zh-Hant': locales_dir / 'zh-Hant.ts',
|
||||
'es-ES': locales_dir / 'es-ES.ts',
|
||||
'ru-RU': locales_dir / 'ru-RU.ts',
|
||||
'th-TH': locales_dir / 'th-TH.ts',
|
||||
'vi-VN': locales_dir / 'vi-VN.ts',
|
||||
}
|
||||
|
||||
results = {}
|
||||
completed_by_lang = {}
|
||||
|
||||
for lang, file_path in language_files.items():
|
||||
translations = completed[lang]
|
||||
if not translations:
|
||||
print(f" ⏭️ 跳过 {lang}(没有已完成的翻译)")
|
||||
results[lang] = (0, [])
|
||||
completed_by_lang[lang] = 0
|
||||
continue
|
||||
|
||||
print(f" 📝 处理 {lang}...")
|
||||
success_count, failed_keys = apply_translations_to_file(file_path, translations, lang)
|
||||
results[lang] = (success_count, failed_keys)
|
||||
completed_by_lang[lang] = success_count
|
||||
|
||||
if success_count > 0:
|
||||
print(f" ✅ 成功应用 {success_count} 个翻译")
|
||||
if failed_keys:
|
||||
print(f" ⚠️ {len(failed_keys)} 个键应用失败")
|
||||
|
||||
# 更新JSON文件中的进度
|
||||
print("\n📊 更新进度信息...")
|
||||
json_path = Path(__file__).parent / 'workflows_translations.json'
|
||||
update_progress_in_json(json_path, completed_by_lang)
|
||||
|
||||
# 生成报告
|
||||
generate_report(results, total_keys)
|
||||
|
||||
print("\n✅ 翻译应用完成!")
|
||||
print("\n💡 提示:")
|
||||
print(" 1. 继续编辑 workflows_translations.json 翻译更多键")
|
||||
print(" 2. 再次运行此脚本应用新的翻译")
|
||||
print(" 3. 重复以上步骤直到所有翻译完成")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
351
web/batch_translate_workflows.py
Normal file
351
web/batch_translate_workflows.py
Normal file
@@ -0,0 +1,351 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
批量翻译workflows的多语言文件
|
||||
支持:西班牙语(es-ES)、俄语(ru-RU)、泰语(th-TH)、越南语(vi-VN)
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
|
||||
# 翻译映射表 - 基于中文原文的专业翻译
|
||||
TRANSLATIONS = {
|
||||
# 基础UI文本
|
||||
"title": {
|
||||
"es-ES": "Conversación de Flujo de Trabajo",
|
||||
"ru-RU": "Диалог Рабочего Процесса",
|
||||
"th-TH": "การสนทนาเวิร์กโฟลว์",
|
||||
"vi-VN": "Hội thoại Quy trình"
|
||||
},
|
||||
"description": {
|
||||
"es-ES": "Descripción",
|
||||
"ru-RU": "Описание",
|
||||
"th-TH": "คำอธิบาย",
|
||||
"vi-VN": "Mô tả"
|
||||
},
|
||||
"createWorkflow": {
|
||||
"es-ES": "Crear Flujo de Trabajo",
|
||||
"ru-RU": "Создать Рабочий Процесс",
|
||||
"th-TH": "สร้างเวิร์กโฟลว์",
|
||||
"vi-VN": "Tạo Quy trình"
|
||||
},
|
||||
"selectFromSidebar": {
|
||||
"es-ES": "Seleccione un flujo de trabajo de la barra lateral",
|
||||
"ru-RU": "Выберите рабочий процесс из боковой панели",
|
||||
"th-TH": "เลือกเวิร์กโฟลว์จากแถบด้านข้าง",
|
||||
"vi-VN": "Chọn một quy trình từ thanh bên"
|
||||
},
|
||||
"editWorkflow": {
|
||||
"es-ES": "Editar Flujo de Trabajo",
|
||||
"ru-RU": "Редактировать Рабочий Процесс",
|
||||
"th-TH": "แก้ไขเวิร์กโฟลว์",
|
||||
"vi-VN": "Chỉnh sửa Quy trình"
|
||||
},
|
||||
"newWorkflow": {
|
||||
"es-ES": "Nuevo Flujo de Trabajo",
|
||||
"ru-RU": "Новый Рабочий Процесс",
|
||||
"th-TH": "เวิร์กโฟลว์ใหม่",
|
||||
"vi-VN": "Quy trình Mới"
|
||||
},
|
||||
"getWorkflowListError": {
|
||||
"es-ES": "Error al obtener la lista de flujos de trabajo:",
|
||||
"ru-RU": "Ошибка получения списка рабочих процессов:",
|
||||
"th-TH": "ไม่สามารถรับรายการเวิร์กโฟลว์:",
|
||||
"vi-VN": "Lỗi khi lấy danh sách quy trình:"
|
||||
},
|
||||
"workflowName": {
|
||||
"es-ES": "Nombre del Flujo de Trabajo",
|
||||
"ru-RU": "Название Рабочего Процесса",
|
||||
"th-TH": "ชื่อเวิร์กโฟลว์",
|
||||
"vi-VN": "Tên Quy trình"
|
||||
},
|
||||
"workflowDescription": {
|
||||
"es-ES": "Descripción del Flujo de Trabajo",
|
||||
"ru-RU": "Описание Рабочего Процесса",
|
||||
"th-TH": "คำอธิบายเวิร์กโฟลว์",
|
||||
"vi-VN": "Mô tả Quy trình"
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def load_translations_json():
|
||||
"""加载翻译JSON文件"""
|
||||
json_path = Path(__file__).parent / 'workflows_translations.json'
|
||||
|
||||
if not json_path.exists():
|
||||
raise FileNotFoundError(f"找不到翻译文件: {json_path}")
|
||||
|
||||
with open(json_path, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def save_translations_json(data):
|
||||
"""保存翻译JSON文件"""
|
||||
json_path = Path(__file__).parent / 'workflows_translations.json'
|
||||
|
||||
with open(json_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
def translate_text(text: str, target_lang: str) -> str:
|
||||
"""
|
||||
翻译文本到目标语言
|
||||
使用规则和模式匹配进行翻译
|
||||
"""
|
||||
# 如果有预定义翻译,直接使用
|
||||
if text in TRANSLATIONS and target_lang in TRANSLATIONS[text]:
|
||||
return TRANSLATIONS[text][target_lang]
|
||||
|
||||
# 通用翻译规则
|
||||
common_translations = {
|
||||
"es-ES": {
|
||||
# 常用词汇
|
||||
"工作流": "Flujo de Trabajo",
|
||||
"节点": "Nodo",
|
||||
"触发器": "Disparador",
|
||||
"执行": "Ejecutar",
|
||||
"调试": "Depurar",
|
||||
"配置": "Configuración",
|
||||
"变量": "Variable",
|
||||
"条件": "Condición",
|
||||
"循环": "Bucle",
|
||||
"等待": "Esperar",
|
||||
"结束": "Fin",
|
||||
"开始": "Inicio",
|
||||
"消息": "Mensaje",
|
||||
"发送": "Enviar",
|
||||
"接收": "Recibir",
|
||||
"错误": "Error",
|
||||
"成功": "Éxito",
|
||||
"失败": "Fallo",
|
||||
"保存": "Guardar",
|
||||
"取消": "Cancelar",
|
||||
"删除": "Eliminar",
|
||||
"编辑": "Editar",
|
||||
"添加": "Agregar",
|
||||
"创建": "Crear",
|
||||
"名称": "Nombre",
|
||||
"描述": "Descripción",
|
||||
"类型": "Tipo",
|
||||
"值": "Valor",
|
||||
"参数": "Parámetro",
|
||||
"输入": "Entrada",
|
||||
"输出": "Salida",
|
||||
"请": "Por favor",
|
||||
"选择": "Seleccionar",
|
||||
"确认": "Confirmar",
|
||||
"提示": "Aviso",
|
||||
"警告": "Advertencia",
|
||||
"信息": "Información",
|
||||
},
|
||||
"ru-RU": {
|
||||
"工作流": "Рабочий Процесс",
|
||||
"节点": "Узел",
|
||||
"触发器": "Триггер",
|
||||
"执行": "Выполнить",
|
||||
"调试": "Отладка",
|
||||
"配置": "Конфигурация",
|
||||
"变量": "Переменная",
|
||||
"条件": "Условие",
|
||||
"循环": "Цикл",
|
||||
"等待": "Ожидание",
|
||||
"结束": "Конец",
|
||||
"开始": "Начало",
|
||||
"消息": "Сообщение",
|
||||
"发送": "Отправить",
|
||||
"接收": "Получить",
|
||||
"错误": "Ошибка",
|
||||
"成功": "Успех",
|
||||
"失败": "Неудача",
|
||||
"保存": "Сохранить",
|
||||
"取消": "Отмена",
|
||||
"删除": "Удалить",
|
||||
"编辑": "Редактировать",
|
||||
"添加": "Добавить",
|
||||
"创建": "Создать",
|
||||
"名称": "Название",
|
||||
"描述": "Описание",
|
||||
"类型": "Тип",
|
||||
"值": "Значение",
|
||||
"参数": "Параметр",
|
||||
"输入": "Вход",
|
||||
"输出": "Выход",
|
||||
"请": "Пожалуйста",
|
||||
"选择": "Выбрать",
|
||||
"确认": "Подтвердить",
|
||||
"提示": "Подсказка",
|
||||
"警告": "Предупреждение",
|
||||
"信息": "Информация",
|
||||
},
|
||||
"th-TH": {
|
||||
"工作流": "เวิร์กโฟลว์",
|
||||
"节点": "โหนด",
|
||||
"触发器": "ทริกเกอร์",
|
||||
"执行": "ดำเนินการ",
|
||||
"调试": "ดีบัก",
|
||||
"配置": "การกำหนดค่า",
|
||||
"变量": "ตัวแปร",
|
||||
"条件": "เงื่อนไข",
|
||||
"循环": "วนซ้ำ",
|
||||
"等待": "รอ",
|
||||
"结束": "สิ้นสุด",
|
||||
"开始": "เริ่มต้น",
|
||||
"消息": "ข้อความ",
|
||||
"发送": "ส่ง",
|
||||
"接收": "รับ",
|
||||
"错误": "ข้อผิดพลาด",
|
||||
"成功": "สำเร็จ",
|
||||
"失败": "ล้มเหลว",
|
||||
"保存": "บันทึก",
|
||||
"取消": "ยกเลิก",
|
||||
"删除": "ลบ",
|
||||
"编辑": "แก้ไข",
|
||||
"添加": "เพิ่ม",
|
||||
"创建": "สร้าง",
|
||||
"名称": "ชื่อ",
|
||||
"描述": "คำอธิบาย",
|
||||
"类型": "ประเภท",
|
||||
"值": "ค่า",
|
||||
"参数": "พารามิเตอร์",
|
||||
"输入": "อินพุต",
|
||||
"输出": "เอาต์พุต",
|
||||
"请": "กรุณา",
|
||||
"选择": "เลือก",
|
||||
"确认": "ยืนยัน",
|
||||
"提示": "คำแนะนำ",
|
||||
"警告": "คำเตือน",
|
||||
"信息": "ข้อมูล",
|
||||
},
|
||||
"vi-VN": {
|
||||
"工作流": "Quy trình",
|
||||
"节点": "Nút",
|
||||
"触发器": "Trình kích hoạt",
|
||||
"执行": "Thực thi",
|
||||
"调试": "Gỡ lỗi",
|
||||
"配置": "Cấu hình",
|
||||
"变量": "Biến",
|
||||
"条件": "Điều kiện",
|
||||
"循环": "Vòng lặp",
|
||||
"等待": "Chờ",
|
||||
"结束": "Kết thúc",
|
||||
"开始": "Bắt đầu",
|
||||
"消息": "Tin nhắn",
|
||||
"发送": "Gửi",
|
||||
"接收": "Nhận",
|
||||
"错误": "Lỗi",
|
||||
"成功": "Thành công",
|
||||
"失败": "Thất bại",
|
||||
"保存": "Lưu",
|
||||
"取消": "Hủy",
|
||||
"删除": "Xóa",
|
||||
"编辑": "Chỉnh sửa",
|
||||
"添加": "Thêm",
|
||||
"创建": "Tạo",
|
||||
"名称": "Tên",
|
||||
"描述": "Mô tả",
|
||||
"类型": "Loại",
|
||||
"值": "Giá trị",
|
||||
"参数": "Tham số",
|
||||
"输入": "Đầu vào",
|
||||
"输出": "Đầu ra",
|
||||
"请": "Vui lòng",
|
||||
"选择": "Chọn",
|
||||
"确认": "Xác nhận",
|
||||
"提示": "Gợi ý",
|
||||
"警告": "Cảnh báo",
|
||||
"信息": "Thông tin",
|
||||
}
|
||||
}
|
||||
|
||||
# 尝试使用通用翻译规则
|
||||
if target_lang in common_translations:
|
||||
result = text
|
||||
for zh, translation in common_translations[target_lang].items():
|
||||
result = result.replace(zh, translation)
|
||||
if result != text:
|
||||
return result
|
||||
|
||||
# 如果没有匹配的翻译规则,返回原文
|
||||
return text
|
||||
|
||||
|
||||
def batch_translate():
|
||||
"""批量翻译所有TODO项"""
|
||||
print("🚀 开始批量翻译workflows...")
|
||||
print("=" * 80)
|
||||
|
||||
# 加载翻译数据
|
||||
data = load_translations_json()
|
||||
translations = data.get('translations', {})
|
||||
|
||||
target_languages = ['es-ES', 'ru-RU', 'th-TH', 'vi-VN']
|
||||
|
||||
stats = {lang: {'total': 0, 'translated': 0} for lang in target_languages}
|
||||
|
||||
# 遍历所有键进行翻译
|
||||
for key, values in translations.items():
|
||||
zh_hans = values.get('zh-Hans', '')
|
||||
|
||||
if not zh_hans or zh_hans == 'TODO':
|
||||
continue
|
||||
|
||||
for lang in target_languages:
|
||||
stats[lang]['total'] += 1
|
||||
|
||||
current_value = values.get(lang, 'TODO')
|
||||
|
||||
# 如果已经翻译过,跳过
|
||||
if current_value != 'TODO' and current_value.strip():
|
||||
stats[lang]['translated'] += 1
|
||||
continue
|
||||
|
||||
# 执行翻译
|
||||
translated = translate_text(zh_hans, lang)
|
||||
|
||||
# 更新翻译
|
||||
if translated and translated != zh_hans:
|
||||
values[lang] = translated
|
||||
stats[lang]['translated'] += 1
|
||||
print(f"✅ [{lang}] {key}: {zh_hans} -> {translated}")
|
||||
|
||||
# 保存更新后的数据
|
||||
save_translations_json(data)
|
||||
|
||||
# 显示统计信息
|
||||
print("\n" + "=" * 80)
|
||||
print("📊 翻译统计:")
|
||||
print("=" * 80)
|
||||
|
||||
for lang in target_languages:
|
||||
total = stats[lang]['total']
|
||||
translated = stats[lang]['translated']
|
||||
percentage = (translated / total * 100) if total > 0 else 0
|
||||
|
||||
lang_names = {
|
||||
'es-ES': '西班牙语',
|
||||
'ru-RU': '俄语',
|
||||
'th-TH': '泰语',
|
||||
'vi-VN': '越南语'
|
||||
}
|
||||
|
||||
print(f"\n【{lang_names[lang]} ({lang})】")
|
||||
print(f" 总计: {total}")
|
||||
print(f" 已翻译: {translated}")
|
||||
print(f" 完成度: {percentage:.1f}%")
|
||||
|
||||
print("\n" + "=" * 80)
|
||||
print("✅ 批量翻译完成!")
|
||||
print("\n💡 下一步:")
|
||||
print(" 1. 运行 python3 check_translation_progress.py 查看进度")
|
||||
print(" 2. 运行 python3 apply_workflows_translations.py 应用翻译")
|
||||
print("=" * 80)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
batch_translate()
|
||||
except Exception as e:
|
||||
print(f"\n❌ 错误: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
280
web/batch_translate_zh_hant.py
Normal file
280
web/batch_translate_zh_hant.py
Normal file
@@ -0,0 +1,280 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
批量翻译繁体中文workflows部分
|
||||
基于简体中文进行繁体转换和台湾用语调整
|
||||
"""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
# 简繁转换映射(常用词汇)
|
||||
SIMPLIFIED_TO_TRADITIONAL = {
|
||||
# 基础词汇
|
||||
'工作流': '工作流程',
|
||||
'创建': '建立',
|
||||
'编辑': '編輯',
|
||||
'删除': '刪除',
|
||||
'保存': '儲存',
|
||||
'加载': '載入',
|
||||
'导出': '匯出',
|
||||
'导入': '匯入',
|
||||
'发布': '發佈',
|
||||
'配置': '設定',
|
||||
'执行': '執行',
|
||||
'记录': '記錄',
|
||||
'编辑器': '編輯器',
|
||||
'调试': '除錯',
|
||||
'基础': '基礎',
|
||||
'信息': '資訊',
|
||||
'设置': '設定',
|
||||
'图标': '圖示',
|
||||
'描述': '描述',
|
||||
'危险': '危險',
|
||||
'区域': '區域',
|
||||
'操作': '操作',
|
||||
'确认': '確認',
|
||||
'名称': '名稱',
|
||||
'输入': '輸入',
|
||||
'启用': '啟用',
|
||||
'加载中': '載入中',
|
||||
'版本': '版本',
|
||||
'创建时间': '建立時間',
|
||||
'更新时间': '更新時間',
|
||||
'统计': '統計',
|
||||
'分析': '分析',
|
||||
'成功': '成功',
|
||||
'失败': '失敗',
|
||||
'平均': '平均',
|
||||
'耗时': '耗時',
|
||||
'筛选': '篩選',
|
||||
'状态': '狀態',
|
||||
'手动': '手動',
|
||||
'触发': '觸發',
|
||||
'开始': '開始',
|
||||
'时间': '時間',
|
||||
'详情': '詳情',
|
||||
'错误': '錯誤',
|
||||
'节点': '節點',
|
||||
'结果': '結果',
|
||||
'等待': '等待',
|
||||
'执行中': '執行中',
|
||||
'已完成': '已完成',
|
||||
'已取消': '已取消',
|
||||
'面板': '面板',
|
||||
'属性': '屬性',
|
||||
'放大': '放大',
|
||||
'缩小': '縮小',
|
||||
'适应': '適應',
|
||||
'视图': '檢視',
|
||||
'未保存': '未儲存',
|
||||
'更改': '變更',
|
||||
'粘贴': '貼上',
|
||||
'已删除': '已刪除',
|
||||
'复制': '複製',
|
||||
'剪贴板': '剪貼簿',
|
||||
'搜索': '搜尋',
|
||||
'正在加载': '正在載入',
|
||||
'类型': '類型',
|
||||
'未找到': '找不到',
|
||||
'清除': '清除',
|
||||
'拖拽': '拖曳',
|
||||
'画布': '畫布',
|
||||
'选择': '選擇',
|
||||
'连线': '連線',
|
||||
'点击': '點擊',
|
||||
'查看': '檢視',
|
||||
'条件': '條件',
|
||||
'已设置': '已設定',
|
||||
'表达式': '運算式',
|
||||
'变量': '變數',
|
||||
'引用': '參照',
|
||||
'上下文': '上下文',
|
||||
'显示': '顯示',
|
||||
'标签': '標籤',
|
||||
'输出': '輸出',
|
||||
'可用': '可用',
|
||||
'全局': '全域',
|
||||
'消息': '訊息',
|
||||
'内容': '內容',
|
||||
'发送者': '發送者',
|
||||
'平台': '平台',
|
||||
'会话': '工作階段',
|
||||
'时间戳': '時間戳記',
|
||||
'无': '無',
|
||||
'选项': '選項',
|
||||
# 节点类型
|
||||
'触发器': '觸發器',
|
||||
'定时': '定時',
|
||||
'事件': '事件',
|
||||
'处理': '處理',
|
||||
'调用': '呼叫',
|
||||
'代码': '程式碼',
|
||||
'模板': '範本',
|
||||
'请求': '請求',
|
||||
'转换': '轉換',
|
||||
'分类器': '分類器',
|
||||
'提取器': '擷取器',
|
||||
'检索': '檢索',
|
||||
'知识库': '知識庫',
|
||||
'聚合': '彙總',
|
||||
'分割': '分割',
|
||||
'赋值': '指派',
|
||||
'控制流': '控制流程',
|
||||
'分支': '分支',
|
||||
'多路': '多路',
|
||||
'循环': '迴圈',
|
||||
'迭代器': '迭代器',
|
||||
'并行': '並行',
|
||||
'延迟': '延遲',
|
||||
'合并': '合併',
|
||||
'聚合器': '彙總器',
|
||||
'动作': '動作',
|
||||
'回复': '回覆',
|
||||
'存储': '儲存',
|
||||
'数据': '資料',
|
||||
'数据库': '資料庫',
|
||||
'集成': '整合',
|
||||
'第三方': '第三方',
|
||||
'查询': '查詢',
|
||||
'缓存': '快取',
|
||||
'工具': '工具',
|
||||
'记忆': '記憶',
|
||||
# 配置字段
|
||||
'关键词': '關鍵字',
|
||||
'过滤': '篩選',
|
||||
'正则': '正規表示式',
|
||||
'长度': '長度',
|
||||
'提及': '提及',
|
||||
'群': '群組',
|
||||
'响应': '回應',
|
||||
'规则': '規則',
|
||||
'访问': '存取',
|
||||
'控制': '控制',
|
||||
'表达式': '運算式',
|
||||
'时区': '時區',
|
||||
'路径': '路徑',
|
||||
'允许': '允許',
|
||||
'方法': '方法',
|
||||
'认证': '驗證',
|
||||
'密钥': '金鑰',
|
||||
'验证': '驗證',
|
||||
'超时': '逾時',
|
||||
'防抖': '防抖',
|
||||
'模型': '模型',
|
||||
'提示词': '提示詞',
|
||||
'系统': '系統',
|
||||
'温度': '溫度',
|
||||
'惩罚': '懲罰',
|
||||
'令牌': '權杖',
|
||||
'序列': '序列',
|
||||
'种子': '種子',
|
||||
'流式': '串流',
|
||||
'历史': '歷史',
|
||||
'语言': '語言',
|
||||
'程序': '程式',
|
||||
'指令': '指令',
|
||||
'参数': '參數',
|
||||
'定义': '定義',
|
||||
'返回': '傳回',
|
||||
'阈值': '閾值',
|
||||
'相似度': '相似度',
|
||||
'引用': '引用',
|
||||
'运算符': '運算子',
|
||||
'迭代': '迭代',
|
||||
'中断': '中斷',
|
||||
'并发': '並行',
|
||||
'等待': '等待',
|
||||
'所有': '所有',
|
||||
'快速': '快速',
|
||||
'单位': '單位',
|
||||
'策略': '策略',
|
||||
'映射': '對應',
|
||||
'模式': '模式',
|
||||
'目标': '目標',
|
||||
'长文本': '長文字',
|
||||
'强制': '強制',
|
||||
'继承': '繼承',
|
||||
'过期': '過期',
|
||||
'前缀': '前置詞',
|
||||
'作用域': '範圍',
|
||||
'建议': '建議',
|
||||
'问题': '問題',
|
||||
'格式': '格式',
|
||||
'连接': '連線',
|
||||
'字符串': '字串',
|
||||
'哈希': '雜湊',
|
||||
'字段': '欄位',
|
||||
'服务器': '伺服器',
|
||||
'应用': '應用程式',
|
||||
'数据集': '資料集',
|
||||
'流程': '流程',
|
||||
'机器人': '機器人',
|
||||
}
|
||||
|
||||
def convert_to_traditional(text: str) -> str:
|
||||
"""将简体中文转换为繁体中文"""
|
||||
result = text
|
||||
for simp, trad in SIMPLIFIED_TO_TRADITIONAL.items():
|
||||
result = result.replace(simp, trad)
|
||||
return result
|
||||
|
||||
def main():
|
||||
print("繁体中文批量转换工具")
|
||||
print("=" * 60)
|
||||
print("此脚本将简体中文翻译转换为繁体中文")
|
||||
print("=" * 60)
|
||||
|
||||
# 读取简体中文文件
|
||||
zh_hans_file = Path(__file__).parent / 'src' / 'i18n' / 'locales' / 'zh-Hans.ts'
|
||||
zh_hant_file = Path(__file__).parent / 'src' / 'i18n' / 'locales' / 'zh-Hant.ts'
|
||||
|
||||
if not zh_hans_file.exists():
|
||||
print(f"错误:找不到简体中文文件 {zh_hans_file}")
|
||||
return
|
||||
|
||||
print(f"读取简体中文文件: {zh_hans_file}")
|
||||
with open(zh_hans_file, 'r', encoding='utf-8') as f:
|
||||
zh_hans_content = f.read()
|
||||
|
||||
# 提取workflows部分
|
||||
workflows_match = re.search(r'workflows:\s*\{(.+?)\n \},\n unifiedBinding:', zh_hans_content, re.DOTALL)
|
||||
if not workflows_match:
|
||||
print("错误:无法找到workflows部分")
|
||||
return
|
||||
|
||||
workflows_content = workflows_match.group(1)
|
||||
print(f"找到workflows部分,长度: {len(workflows_content)} 字符")
|
||||
|
||||
# 转换为繁体
|
||||
print("正在转换为繁体中文...")
|
||||
traditional_content = convert_to_traditional(workflows_content)
|
||||
|
||||
# 读取现有的繁体中文文件
|
||||
print(f"读取繁体中文文件: {zh_hant_file}")
|
||||
with open(zh_hant_file, 'r', encoding='utf-8') as f:
|
||||
zh_hant_content = f.read()
|
||||
|
||||
# 替换workflows部分
|
||||
zh_hant_new = re.sub(
|
||||
r'workflows:\s*\{(.+?)\n \},\n unifiedBinding:',
|
||||
f'workflows: {{\n{traditional_content}\n }},\n unifiedBinding:',
|
||||
zh_hant_content,
|
||||
flags=re.DOTALL
|
||||
)
|
||||
|
||||
# 写入文件
|
||||
print(f"写入繁体中文文件: {zh_hant_file}")
|
||||
with open(zh_hant_file, 'w', encoding='utf-8') as f:
|
||||
f.write(zh_hant_new)
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("✅ 繁体中文转换完成!")
|
||||
print("=" * 60)
|
||||
print(f"已更新文件: {zh_hant_file}")
|
||||
print("\n建议:")
|
||||
print("1. 检查转换结果是否正确")
|
||||
print("2. 运行 TypeScript 编译检查语法")
|
||||
print("3. 手动审核关键术语的翻译")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
56
web/check_ru_ts_file.py
Normal file
56
web/check_ru_ts_file.py
Normal file
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env python3
|
||||
"""检查 ru-RU.ts 文件中的中文字符问题"""
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
def contains_chinese(text: str) -> bool:
|
||||
"""检查文本是否包含中文字符"""
|
||||
return bool(re.search(r'[\u4e00-\u9fff]', text))
|
||||
|
||||
def main():
|
||||
ts_file = Path(__file__).parent / "src/i18n/locales/ru-RU.ts"
|
||||
|
||||
with open(ts_file, 'r', encoding='utf-8') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
problematic_lines = []
|
||||
in_workflows = False
|
||||
|
||||
for i, line in enumerate(lines, 1):
|
||||
# 检测是否进入 workflows 部分
|
||||
if 'workflows:' in line:
|
||||
in_workflows = True
|
||||
elif in_workflows and line.strip().startswith('}') and line.count('}') > line.count('{'):
|
||||
# 可能退出 workflows 部分
|
||||
pass
|
||||
|
||||
# 检查是否包含中文字符
|
||||
if contains_chinese(line):
|
||||
# 提取键名
|
||||
key_match = re.search(r'(\w+):\s*[\'"]', line)
|
||||
key_name = key_match.group(1) if key_match else 'unknown'
|
||||
|
||||
problematic_lines.append({
|
||||
'line_num': i,
|
||||
'key': key_name,
|
||||
'content': line.strip(),
|
||||
'in_workflows': in_workflows
|
||||
})
|
||||
|
||||
print(f"发现 {len(problematic_lines)} 行包含中文字符的俄语翻译:\n")
|
||||
|
||||
# 只显示 workflows 部分的问题
|
||||
workflows_problems = [p for p in problematic_lines if p['in_workflows']]
|
||||
|
||||
print(f"workflows 部分问题: {len(workflows_problems)} 行\n")
|
||||
|
||||
for item in workflows_problems[:30]: # 显示前30个
|
||||
print(f"行 {item['line_num']}: {item['key']}")
|
||||
print(f" {item['content'][:100]}")
|
||||
print()
|
||||
|
||||
if len(workflows_problems) > 30:
|
||||
print(f"... 还有 {len(workflows_problems) - 30} 个问题行")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
44
web/check_russian_translations.py
Normal file
44
web/check_russian_translations.py
Normal file
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python3
|
||||
"""检查俄语翻译中的中文字符问题"""
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
def contains_chinese(text: str) -> bool:
|
||||
"""检查文本是否包含中文字符"""
|
||||
return bool(re.search(r'[\u4e00-\u9fff]', text))
|
||||
|
||||
def main():
|
||||
json_path = Path(__file__).parent / "workflows_translations.json"
|
||||
|
||||
with open(json_path, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
|
||||
translations = data.get('translations', {})
|
||||
|
||||
problematic_keys = []
|
||||
|
||||
for key, value in translations.items():
|
||||
if isinstance(value, dict) and 'ru-RU' in value:
|
||||
ru_text = value['ru-RU']
|
||||
if ru_text != 'TODO' and contains_chinese(ru_text):
|
||||
zh_hans = value.get('zh-Hans', '')
|
||||
problematic_keys.append({
|
||||
'key': key,
|
||||
'ru_text': ru_text,
|
||||
'zh_hans': zh_hans
|
||||
})
|
||||
|
||||
print(f"发现 {len(problematic_keys)} 个包含中文字符的俄语翻译:\n")
|
||||
|
||||
for item in problematic_keys[:20]: # 只显示前20个
|
||||
print(f"键: {item['key']}")
|
||||
print(f" 中文原文: {item['zh_hans']}")
|
||||
print(f" 俄语翻译: {item['ru_text']}")
|
||||
print()
|
||||
|
||||
if len(problematic_keys) > 20:
|
||||
print(f"... 还有 {len(problematic_keys) - 20} 个问题翻译")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
202
web/check_translation_progress.py
Normal file
202
web/check_translation_progress.py
Normal file
@@ -0,0 +1,202 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
检查workflows翻译进度
|
||||
|
||||
使用方法:
|
||||
python3 check_translation_progress.py
|
||||
|
||||
显示:
|
||||
- 每种语言的翻译进度
|
||||
- 已完成和待完成的键数量
|
||||
- 翻译完成百分比
|
||||
- 最近翻译的键(如果有)
|
||||
"""
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Tuple
|
||||
|
||||
|
||||
def load_translations() -> Dict:
|
||||
"""加载翻译JSON文件"""
|
||||
json_path = Path(__file__).parent / 'workflows_translations.json'
|
||||
|
||||
if not json_path.exists():
|
||||
raise FileNotFoundError(f"找不到翻译文件: {json_path}")
|
||||
|
||||
with open(json_path, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def analyze_progress(translations_data: Dict) -> Dict[str, Dict]:
|
||||
"""
|
||||
分析每种语言的翻译进度
|
||||
|
||||
返回格式:
|
||||
{
|
||||
'ja-JP': {
|
||||
'completed': 50,
|
||||
'total': 627,
|
||||
'percentage': 7.97,
|
||||
'completed_keys': ['title', 'description', ...],
|
||||
'pending_keys': ['...', ...]
|
||||
},
|
||||
...
|
||||
}
|
||||
"""
|
||||
translations = translations_data.get('translations', {})
|
||||
languages = ['ja-JP', 'zh-Hant', 'es-ES', 'ru-RU', 'th-TH', 'vi-VN']
|
||||
|
||||
progress = {}
|
||||
|
||||
for lang in languages:
|
||||
completed_keys = []
|
||||
pending_keys = []
|
||||
|
||||
for key, values in translations.items():
|
||||
translation = values.get(lang, 'TODO')
|
||||
if translation != 'TODO' and translation.strip():
|
||||
completed_keys.append(key)
|
||||
else:
|
||||
pending_keys.append(key)
|
||||
|
||||
total = len(translations)
|
||||
completed = len(completed_keys)
|
||||
percentage = (completed / total * 100) if total > 0 else 0
|
||||
|
||||
progress[lang] = {
|
||||
'completed': completed,
|
||||
'total': total,
|
||||
'percentage': percentage,
|
||||
'completed_keys': completed_keys,
|
||||
'pending_keys': pending_keys
|
||||
}
|
||||
|
||||
return progress
|
||||
|
||||
|
||||
def get_language_name(lang_code: str) -> str:
|
||||
"""获取语言的中文名称"""
|
||||
names = {
|
||||
'ja-JP': '日语',
|
||||
'zh-Hant': '繁体中文',
|
||||
'es-ES': '西班牙语',
|
||||
'ru-RU': '俄语',
|
||||
'th-TH': '泰语',
|
||||
'vi-VN': '越南语'
|
||||
}
|
||||
return names.get(lang_code, lang_code)
|
||||
|
||||
|
||||
def print_progress_bar(percentage: float, width: int = 40) -> str:
|
||||
"""生成进度条"""
|
||||
filled = int(width * percentage / 100)
|
||||
bar = '█' * filled + '░' * (width - filled)
|
||||
return f"[{bar}] {percentage:.1f}%"
|
||||
|
||||
|
||||
def display_progress(progress: Dict[str, Dict]):
|
||||
"""显示翻译进度"""
|
||||
print("\n" + "="*80)
|
||||
print("📊 Workflows翻译进度报告")
|
||||
print("="*80)
|
||||
|
||||
# 按完成度排序
|
||||
sorted_langs = sorted(progress.items(), key=lambda x: x[1]['percentage'], reverse=True)
|
||||
|
||||
for lang, data in sorted_langs:
|
||||
lang_name = get_language_name(lang)
|
||||
completed = data['completed']
|
||||
total = data['total']
|
||||
percentage = data['percentage']
|
||||
|
||||
print(f"\n【{lang_name} ({lang})】")
|
||||
print(f" {print_progress_bar(percentage)}")
|
||||
print(f" ✅ 已完成: {completed}/{total}")
|
||||
print(f" ⏳ 待翻译: {total - completed}")
|
||||
|
||||
# 显示最近完成的键(前5个)
|
||||
if data['completed_keys']:
|
||||
recent = data['completed_keys'][:5]
|
||||
print(f" 📝 最近完成: {', '.join(recent)}")
|
||||
if len(data['completed_keys']) > 5:
|
||||
print(f" (还有 {len(data['completed_keys']) - 5} 个已完成)")
|
||||
|
||||
# 总体统计
|
||||
print("\n" + "-"*80)
|
||||
total_completed = sum(d['completed'] for d in progress.values())
|
||||
total_items = sum(d['total'] for d in progress.values())
|
||||
avg_percentage = (total_completed / total_items * 100) if total_items > 0 else 0
|
||||
|
||||
print(f"📈 总体进度: {total_completed}/{total_items} ({avg_percentage:.1f}%)")
|
||||
print(f"📊 平均每种语言: {total_completed // len(progress)}/{progress[list(progress.keys())[0]]['total']}")
|
||||
|
||||
# 估算剩余工作量
|
||||
remaining = total_items - total_completed
|
||||
print(f"\n💡 剩余工作量: {remaining} 个翻译项")
|
||||
|
||||
if remaining > 0:
|
||||
# 建议分批策略
|
||||
batches_50 = (remaining + 49) // 50 # 向上取整
|
||||
batches_100 = (remaining + 99) // 100
|
||||
|
||||
print(f" 建议分批策略:")
|
||||
print(f" - 每批50个键: 需要 {batches_50} 批")
|
||||
print(f" - 每批100个键: 需要 {batches_100} 批")
|
||||
|
||||
print("="*80)
|
||||
|
||||
|
||||
def suggest_next_keys(progress: Dict[str, Dict], batch_size: int = 50) -> List[str]:
|
||||
"""建议下一批要翻译的键"""
|
||||
# 找出所有语言都还没翻译的键
|
||||
all_pending = set()
|
||||
|
||||
for lang, data in progress.items():
|
||||
if not all_pending:
|
||||
all_pending = set(data['pending_keys'])
|
||||
else:
|
||||
all_pending &= set(data['pending_keys'])
|
||||
|
||||
return list(all_pending)[:batch_size]
|
||||
|
||||
|
||||
def main():
|
||||
print("🔍 正在检查翻译进度...")
|
||||
|
||||
try:
|
||||
# 加载翻译数据
|
||||
translations_data = load_translations()
|
||||
|
||||
# 分析进度
|
||||
progress = analyze_progress(translations_data)
|
||||
|
||||
# 显示进度
|
||||
display_progress(progress)
|
||||
|
||||
# 建议下一批翻译的键
|
||||
next_keys = suggest_next_keys(progress, batch_size=50)
|
||||
|
||||
if next_keys:
|
||||
print(f"\n💡 建议下一批翻译的键(前10个):")
|
||||
for i, key in enumerate(next_keys[:10], 1):
|
||||
print(f" {i}. {key}")
|
||||
|
||||
if len(next_keys) > 10:
|
||||
print(f" ... 还有 {len(next_keys) - 10} 个键")
|
||||
else:
|
||||
print("\n🎉 恭喜!所有翻译已完成!")
|
||||
|
||||
print("\n✅ 进度检查完成")
|
||||
|
||||
except FileNotFoundError as e:
|
||||
print(f"\n❌ 错误: {e}")
|
||||
print("💡 请确保 workflows_translations.json 文件存在")
|
||||
except Exception as e:
|
||||
print(f"\n❌ 发生错误: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
160
web/fix_i18n_keys.py
Normal file
160
web/fix_i18n_keys.py
Normal file
@@ -0,0 +1,160 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
自动修复所有语言文件中缺失的 nodeOutputs 和 nodeInputs 键
|
||||
"""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
# 需要添加的 nodeOutputs 键(使用英文作为临时翻译)
|
||||
MISSING_NODE_OUTPUTS = {
|
||||
'sender': 'Sender',
|
||||
'output': 'Output',
|
||||
'result': 'Result',
|
||||
'data': 'Data',
|
||||
'error': 'Error Message',
|
||||
'success': 'Success Status',
|
||||
'event': 'Event',
|
||||
'trigger_time': 'Trigger Time',
|
||||
'logs': 'Logs',
|
||||
'scores': 'Scores',
|
||||
'missing': 'Missing Parameters',
|
||||
'parsed': 'Parsed Result',
|
||||
'chunks': 'Text Chunks',
|
||||
'text': 'Text Content',
|
||||
'case_1': 'Case 1 Output',
|
||||
'case_2': 'Case 2 Output',
|
||||
'branch_1': 'Branch 1 Output',
|
||||
'branch_2': 'Branch 2 Output',
|
||||
'count': 'Count',
|
||||
'execution_id': 'Execution ID',
|
||||
'notification_id': 'Notification ID',
|
||||
'suggestions': 'Suggestions',
|
||||
'embedding': 'Embedding Vector',
|
||||
'dimensions': 'Vector Dimensions',
|
||||
'intent': 'Intent',
|
||||
'entities': 'Entities',
|
||||
}
|
||||
|
||||
# 需要添加的 nodeInputs 键
|
||||
MISSING_NODE_INPUTS = {
|
||||
'payload': 'Payload',
|
||||
'input_value': 'Input Value',
|
||||
'conversation_id': 'Conversation ID',
|
||||
}
|
||||
|
||||
# 需要修复的语言文件
|
||||
LANGUAGE_FILES = [
|
||||
'ja-JP.ts',
|
||||
'zh-Hant.ts',
|
||||
'es-ES.ts',
|
||||
'ru-RU.ts',
|
||||
'th-TH.ts',
|
||||
'vi-VN.ts',
|
||||
]
|
||||
|
||||
def find_insertion_point(content: str, section: str) -> tuple[int, str]:
|
||||
"""
|
||||
找到插入点的位置
|
||||
section: 'nodeOutputs' 或 'nodeInputs'
|
||||
返回: (插入位置的行号, 缩进字符串)
|
||||
"""
|
||||
lines = content.split('\n')
|
||||
in_section = False
|
||||
last_key_line = -1
|
||||
indent = ' '
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
if f'{section}:' in line and '{' in line:
|
||||
in_section = True
|
||||
continue
|
||||
|
||||
if in_section:
|
||||
# 检测缩进
|
||||
if line.strip() and not line.strip().startswith('//'):
|
||||
match = re.match(r'^(\s+)', line)
|
||||
if match:
|
||||
indent = match.group(1)
|
||||
|
||||
# 找到最后一个键值对
|
||||
if ':' in line and not line.strip().startswith('//'):
|
||||
last_key_line = i
|
||||
|
||||
# 遇到闭合括号,说明section结束
|
||||
if '},' in line and last_key_line > 0:
|
||||
return last_key_line, indent
|
||||
|
||||
return -1, indent
|
||||
|
||||
def add_missing_keys(file_path: Path):
|
||||
"""为指定的语言文件添加缺失的键"""
|
||||
print(f"\n处理文件: {file_path.name}")
|
||||
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
lines = content.split('\n')
|
||||
modified = False
|
||||
|
||||
# 处理 nodeOutputs
|
||||
for key, value in MISSING_NODE_OUTPUTS.items():
|
||||
if f" {key}:" not in content:
|
||||
print(f" 添加 nodeOutputs.{key}")
|
||||
|
||||
# 找到插入点
|
||||
insert_line, indent = find_insertion_point(content, 'nodeOutputs')
|
||||
if insert_line > 0:
|
||||
# 在最后一个键之后插入
|
||||
new_line = f"{indent}{key}: '{value}',"
|
||||
lines.insert(insert_line + 1, new_line)
|
||||
content = '\n'.join(lines)
|
||||
lines = content.split('\n')
|
||||
modified = True
|
||||
|
||||
# 处理 nodeInputs
|
||||
for key, value in MISSING_NODE_INPUTS.items():
|
||||
if f" {key}:" not in content:
|
||||
print(f" 添加 nodeInputs.{key}")
|
||||
|
||||
# 找到插入点
|
||||
insert_line, indent = find_insertion_point(content, 'nodeInputs')
|
||||
if insert_line > 0:
|
||||
# 在最后一个键之后插入
|
||||
new_line = f"{indent}{key}: '{value}',"
|
||||
lines.insert(insert_line + 1, new_line)
|
||||
content = '\n'.join(lines)
|
||||
lines = content.split('\n')
|
||||
modified = True
|
||||
|
||||
if modified:
|
||||
# 写回文件
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.write('\n'.join(lines))
|
||||
print(f" ✓ 已更新 {file_path.name}")
|
||||
else:
|
||||
print(f" - {file_path.name} 无需更新")
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
locales_dir = Path(__file__).parent / 'src' / 'i18n' / 'locales'
|
||||
|
||||
print("=" * 60)
|
||||
print("开始修复多语言 i18n 文件")
|
||||
print("=" * 60)
|
||||
|
||||
for lang_file in LANGUAGE_FILES:
|
||||
file_path = locales_dir / lang_file
|
||||
if file_path.exists():
|
||||
add_missing_keys(file_path)
|
||||
else:
|
||||
print(f"\n警告: 文件不存在 - {lang_file}")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("修复完成!")
|
||||
print("=" * 60)
|
||||
print(f"\n已处理 {len(LANGUAGE_FILES)} 个语言文件")
|
||||
print(f"添加了 {len(MISSING_NODE_OUTPUTS)} 个 nodeOutputs 键")
|
||||
print(f"添加了 {len(MISSING_NODE_INPUTS)} 个 nodeInputs 键")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
237
web/fix_ru_mixed_text.py
Normal file
237
web/fix_ru_mixed_text.py
Normal file
@@ -0,0 +1,237 @@
|
||||
#!/usr/bin/env python3
|
||||
"""直接修复 ru-RU.ts 文件中包含中文字符的翻译
|
||||
从 en-US.ts 提取英文原文,然后替换 ru-RU.ts 中的混合文本
|
||||
"""
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Tuple
|
||||
|
||||
def contains_chinese(text: str) -> bool:
|
||||
"""检查文本是否包含中文字符"""
|
||||
return bool(re.search(r'[\u4e00-\u9fff]', text))
|
||||
|
||||
def extract_key_value_pairs(file_path: Path, section_name: str = 'workflows') -> Dict[str, str]:
|
||||
"""从 .ts 文件中提取指定部分的键值对"""
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
result = {}
|
||||
in_section = False
|
||||
brace_count = 0
|
||||
|
||||
for line in lines:
|
||||
# 检测进入目标部分
|
||||
if f'{section_name}:' in line and '{' in line:
|
||||
in_section = True
|
||||
brace_count = line.count('{') - line.count('}')
|
||||
continue
|
||||
|
||||
if not in_section:
|
||||
continue
|
||||
|
||||
# 更新括号计数
|
||||
brace_count += line.count('{') - line.count('}')
|
||||
|
||||
# 提取键值对
|
||||
match = re.match(r'\s*(\w+):\s*[\'"]([^\'"]*(?:\\.[^\'"]*)*)[\'"],?\s*(?://.*)?$', line)
|
||||
if match:
|
||||
key = match.group(1)
|
||||
value = match.group(2)
|
||||
# 处理转义字符
|
||||
value = value.replace("\\'", "'").replace('\\"', '"').replace('\\n', '\n')
|
||||
result[key] = value
|
||||
|
||||
# 检测退出部分
|
||||
if brace_count == 0:
|
||||
break
|
||||
|
||||
return result
|
||||
|
||||
def find_problematic_lines(file_path: Path) -> List[Tuple[int, str, str]]:
|
||||
"""找出包含中文字符的行"""
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
problematic = []
|
||||
in_workflows = False
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
if 'workflows:' in line:
|
||||
in_workflows = True
|
||||
continue
|
||||
|
||||
if in_workflows and contains_chinese(line):
|
||||
# 提取键名
|
||||
match = re.match(r'\s*(\w+):\s*[\'"]', line)
|
||||
if match:
|
||||
key = match.group(1)
|
||||
problematic.append((i, key, line))
|
||||
|
||||
return problematic
|
||||
|
||||
# 手动翻译映射 - 基于常见的中俄混合模式
|
||||
MANUAL_TRANSLATIONS = {
|
||||
# 率 -> процент/коэффициент
|
||||
'成功率': 'Процент успеха',
|
||||
'失败率': 'Процент неудач',
|
||||
|
||||
# 次数 -> количество/раз
|
||||
'失败次数': 'Количество неудач',
|
||||
'执行次数': 'Количество выполнений',
|
||||
'成功次数': 'Количество успехов',
|
||||
|
||||
# 状态 -> статус
|
||||
'全部状态': 'Все статусы',
|
||||
'运行状态': 'Статус выполнения',
|
||||
|
||||
# 时间 -> время
|
||||
'等待时间': 'Время ожидания',
|
||||
'执行时间': 'Время выполнения',
|
||||
'创建时间': 'Время создания',
|
||||
'更新时间': 'Время обновления',
|
||||
|
||||
# 记录 -> запись
|
||||
'执行记录': 'Записи выполнения',
|
||||
'节点执行记录': 'Записи выполнения узлов',
|
||||
|
||||
# 节点 -> узел
|
||||
'节点': 'Узел',
|
||||
'节点类型': 'Тип узла',
|
||||
'节点名称': 'Название узла',
|
||||
'选中节点': 'Выбранный узел',
|
||||
'搜索节点': 'Поиск узлов',
|
||||
'没有选中节点可复制': 'Нет выбранных узлов для копирования',
|
||||
'已复制节点': 'Узлы скопированы',
|
||||
'已粘贴节点': 'Узлы вставлены',
|
||||
'未找到匹配节点': 'Совпадающие узлы не найдены',
|
||||
'拖拽节点到画布添加': 'Перетащите узел на холст для добавления',
|
||||
'选择节点或连线': 'Выберите узел или соединение',
|
||||
'点击画布中节点或连线来查看和编辑其属性': 'Нажмите на узел или соединение на холсте, чтобы просмотреть и изменить его свойства',
|
||||
'该节点类型暂无配置选项': 'Для этого типа узла пока нет параметров конфигурации',
|
||||
'确定删除此节点': 'Вы уверены, что хотите удалить этот узел?',
|
||||
'删除后,该节点及其所有连线将被永久移除': 'После удаления узел и все его соединения будут удалены навсегда',
|
||||
'节点显示名称': 'Отображаемое имя узла',
|
||||
|
||||
# 工作流 -> рабочий процесс
|
||||
'工作流': 'Рабочий процесс',
|
||||
'工作流名称': 'Название рабочего процесса',
|
||||
'输入工作流名称': 'Введите название рабочего процесса',
|
||||
|
||||
# 变量 -> переменная
|
||||
'变量': 'Переменная',
|
||||
'变量名': 'Имя переменной',
|
||||
'上下文变量': 'Контекстная переменная',
|
||||
|
||||
# 条件 -> условие
|
||||
'条件': 'Условие',
|
||||
'条件分支': 'Условная ветвь',
|
||||
'输入条件表达式': 'Введите условное выражение',
|
||||
'条件为空时': 'Когда условие пусто',
|
||||
'条件为空时,该连线将始终被执行': 'Когда условие пусто, это соединение всегда будет выполняться',
|
||||
|
||||
# 其他常见词汇
|
||||
'已配置': 'Настроено',
|
||||
'未保存': 'Не сохранено',
|
||||
'有未保存更改': 'Есть несохраненные изменения',
|
||||
'剪贴板为空': 'Буфер обмена пуст',
|
||||
'正在加载节点类型': 'Загрузка типов узлов',
|
||||
'模拟消息': 'Имитация сообщения',
|
||||
'调试上下文': 'Контекст отладки',
|
||||
'请求体': 'Тело запроса',
|
||||
'确定删除此连线': 'Вы уверены, что хотите удалить это соединение?',
|
||||
'删除后,该连线将被永久移除': 'После удаления соединение будет удалено навсегда',
|
||||
}
|
||||
|
||||
def create_translation_dict() -> Dict[str, str]:
|
||||
"""创建完整的翻译字典,包括部分匹配"""
|
||||
translations = {}
|
||||
|
||||
# 添加手动翻译
|
||||
for zh, ru in MANUAL_TRANSLATIONS.items():
|
||||
translations[zh] = ru
|
||||
|
||||
return translations
|
||||
|
||||
def fix_mixed_text(text: str, translations: Dict[str, str]) -> str:
|
||||
"""修复混合中俄文本"""
|
||||
# 首先尝试完全匹配
|
||||
for zh, ru in translations.items():
|
||||
if zh in text:
|
||||
text = text.replace(zh, ru)
|
||||
|
||||
# 移除剩余的中文字符(如果还有的话)
|
||||
# 这是最后的手段,用俄语占位符替换
|
||||
if contains_chinese(text):
|
||||
# 提取中文部分并尝试翻译
|
||||
chinese_parts = re.findall(r'[\u4e00-\u9fff]+', text)
|
||||
for part in chinese_parts:
|
||||
if part in translations:
|
||||
text = text.replace(part, translations[part])
|
||||
|
||||
return text
|
||||
|
||||
def main():
|
||||
base_dir = Path(__file__).parent
|
||||
en_us_file = base_dir / "src/i18n/locales/en-US.ts"
|
||||
ru_ru_file = base_dir / "src/i18n/locales/ru-RU.ts"
|
||||
|
||||
print("🔍 正在分析 ru-RU.ts 文件...")
|
||||
|
||||
# 找出有问题的行
|
||||
problematic_lines = find_problematic_lines(ru_ru_file)
|
||||
print(f"发现 {len(problematic_lines)} 行包含中文字符")
|
||||
|
||||
if not problematic_lines:
|
||||
print("✅ 没有需要修复的翻译!")
|
||||
return
|
||||
|
||||
# 读取整个文件
|
||||
with open(ru_ru_file, 'r', encoding='utf-8') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
# 创建翻译字典
|
||||
translations = create_translation_dict()
|
||||
|
||||
# 修复每一行
|
||||
fixed_count = 0
|
||||
for line_num, key, original_line in problematic_lines:
|
||||
# 提取当前值
|
||||
match = re.match(r'(\s*)(\w+):\s*[\'"]([^\'"]*(?:\\.[^\'"]*)*)[\'"],?(\s*(?://.*)?)\s*$', original_line)
|
||||
if not match:
|
||||
continue
|
||||
|
||||
indent, key_name, value, comment = match.groups()
|
||||
|
||||
# 修复值
|
||||
fixed_value = fix_mixed_text(value, translations)
|
||||
|
||||
if fixed_value != value and not contains_chinese(fixed_value):
|
||||
# 重建行
|
||||
new_line = f"{indent}{key_name}: '{fixed_value}',{comment}\n"
|
||||
lines[line_num] = new_line
|
||||
fixed_count += 1
|
||||
print(f"✓ 修复 {key_name}: {value[:50]}... -> {fixed_value[:50]}...")
|
||||
|
||||
# 写回文件
|
||||
if fixed_count > 0:
|
||||
with open(ru_ru_file, 'w', encoding='utf-8') as f:
|
||||
f.writelines(lines)
|
||||
print(f"\n✅ 成功修复 {fixed_count} 行翻译")
|
||||
print(f"📝 已更新文件: {ru_ru_file}")
|
||||
else:
|
||||
print("\n⚠️ 没有成功修复任何翻译")
|
||||
print("可能需要手动检查或添加更多翻译映射")
|
||||
|
||||
# 再次检查
|
||||
print("\n🔍 验证修复结果...")
|
||||
remaining_problems = find_problematic_lines(ru_ru_file)
|
||||
if remaining_problems:
|
||||
print(f"⚠️ 仍有 {len(remaining_problems)} 行包含中文字符")
|
||||
print("\n前10个未修复的键:")
|
||||
for i, (_, key, line) in enumerate(remaining_problems[:10], 1):
|
||||
print(f" {i}. {key}: {line.strip()[:80]}")
|
||||
else:
|
||||
print("✅ 所有中文字符已清除!")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
108
web/fix_ru_workflows.py
Normal file
108
web/fix_ru_workflows.py
Normal file
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
修复俄语翻译文件中 workflows 部分的中文混杂问题
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
# 读取俄语文件
|
||||
with open('src/i18n/locales/ru-RU.ts', 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# 定义需要替换的中文文本到俄语的映射
|
||||
replacements = {
|
||||
# workflows section
|
||||
'工作流': 'Рабочий процесс',
|
||||
'创建和管理可视化工作流,实现复杂的消息处理逻辑': 'Создание и управление визуальными рабочими процессами для реализации сложной логики обработки сообщений',
|
||||
'创建工作流': 'Создать рабочий процесс',
|
||||
'从侧边栏选择一个工作流': 'Выберите рабочий процесс из боковой панели',
|
||||
'编辑工作流': 'Редактировать рабочий процесс',
|
||||
'新工作流': 'Новый рабочий процесс',
|
||||
'获取工作流列表失败:': 'Ошибка получения списка рабочих процессов: ',
|
||||
'工作流名称': 'Название рабочего процесса',
|
||||
'工作流描述': 'Описание рабочего процесса',
|
||||
'工作流名称不能为空': 'Название рабочего процесса не может быть пустым',
|
||||
'一个工作流': 'Рабочий процесс',
|
||||
'获取工作流失败:': 'Ошибка получения рабочего процесса: ',
|
||||
'加载工作流失败': 'Ошибка загрузки рабочего процесса',
|
||||
'保存成功': 'Успешно сохранено',
|
||||
'保存失败:': 'Ошибка сохранения: ',
|
||||
'工作流创建成功': 'Рабочий процесс успешно создан',
|
||||
'创建失败:': 'Ошибка создания: ',
|
||||
'删除成功': 'Успешно удалено',
|
||||
'删除失败:': 'Ошибка удаления: ',
|
||||
'你确定要删除这个工作流吗?': 'Вы уверены, что хотите удалить этот рабочий процесс?',
|
||||
'复制成功': 'Успешно скопировано',
|
||||
'复制失败:': 'Ошибка копирования: ',
|
||||
'导出': 'Экспорт',
|
||||
'导入': 'Импорт',
|
||||
'工作流已导出': 'Рабочий процесс экспортирован',
|
||||
'工作流已导入': 'Рабочий процесс импортирован',
|
||||
'导入失败:文件格式无效': 'Ошибка импорта: недопустимый формат файла',
|
||||
'发布': 'Опубликовать',
|
||||
'发布成功': 'Успешно опубликовано',
|
||||
'发布失败': 'Ошибка публикации',
|
||||
'配置': 'Конфигурация',
|
||||
'执行记录': 'Записи выполнения',
|
||||
'编辑器': 'Редактор',
|
||||
'对话调试': 'Отладка диалога',
|
||||
'基础信息': 'Основная информация',
|
||||
'设置工作流名称、图标和描述': 'Настроить название, значок и описание рабочего процесса',
|
||||
'设置工作流名称和描述': 'Настроить название и описание рабочего процесса',
|
||||
'危险区域': 'Опасная зона',
|
||||
'不可逆的操作': 'Необратимые операции',
|
||||
'删除此工作流': 'Удалить этот рабочий процесс',
|
||||
'删除后,所有关联配置将被永久移除,且无法恢复。': 'После удаления все связанные конфигурации будут удалены навсегда и не могут быть восстановлены.',
|
||||
'删除工作流': 'Удалить рабочий процесс',
|
||||
'确认删除': 'Подтвердить удаление',
|
||||
'您确定要删除工作流': 'Вы уверены, что хотите удалить рабочий процесс',
|
||||
'吗?此操作无法撤销。': '? Эту операцию нельзя отменить.',
|
||||
'名称': 'Название',
|
||||
'输入工作流名称': 'Введите название рабочего процесса',
|
||||
'输入工作流描述(可选)': 'Введите описание рабочего процесса (необязательно)',
|
||||
'启用': 'Включить',
|
||||
'启用后,工作流将可以被触发执行': 'После включения рабочий процесс может быть запущен для выполнения',
|
||||
'加载中...': 'Загрузка...',
|
||||
'工作流信息': 'Информация о рабочем процессе',
|
||||
'版本': 'Версия',
|
||||
'创建时间': 'Дата создания',
|
||||
'更新时间': 'Дата обновления',
|
||||
'共': 'Всего',
|
||||
'条执行记录': 'записей выполнения',
|
||||
'统计分析': 'Статистический анализ',
|
||||
'成功': 'успешных',
|
||||
'次': 'раз',
|
||||
'成功率': 'Успешность',
|
||||
'平均耗时': 'Средняя длительность',
|
||||
'每次执行': 'За выполнение',
|
||||
'失败次数': 'Количество неудач',
|
||||
'最后执行': 'Последнее выполнение',
|
||||
'按状态筛选': 'Фильтр по статусу',
|
||||
'全部状态': 'Все статусы',
|
||||
'手动触发': 'Ручной запуск',
|
||||
'执行': 'Выполнение',
|
||||
'状态': 'Статус',
|
||||
'触发类型': 'Тип триггера',
|
||||
'开始时间': 'Время начала',
|
||||
'耗时': 'Длительность',
|
||||
'暂无执行记录': 'Нет записей выполнения',
|
||||
'执行详情': 'Детали выполнения',
|
||||
'错误信息': 'Информация об ошибке',
|
||||
'节点执行记录': 'Записи выполнения узлов',
|
||||
'执行结果': 'Результат выполнения',
|
||||
'等待中': 'Ожидание',
|
||||
'执行中': 'Выполнение',
|
||||
'已完成': 'Завершено',
|
||||
'失败': 'Неудача',
|
||||
'已取消': 'Отменено',
|
||||
}
|
||||
|
||||
# 执行替换
|
||||
for chinese, russian in replacements.items():
|
||||
content = content.replace(chinese, russian)
|
||||
|
||||
# 写回文件
|
||||
with open('src/i18n/locales/ru-RU.ts', 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
|
||||
print("俄语翻译文件修复完成!")
|
||||
150
web/fix_russian_translations.py
Normal file
150
web/fix_russian_translations.py
Normal file
@@ -0,0 +1,150 @@
|
||||
#!/usr/bin/env python3
|
||||
"""修复 ru-RU.ts 文件中包含中文字符的翻译"""
|
||||
import re
|
||||
import os
|
||||
from pathlib import Path
|
||||
from anthropic import Anthropic
|
||||
|
||||
def contains_chinese(text: str) -> bool:
|
||||
"""检查文本是否包含中文字符"""
|
||||
return bool(re.search(r'[\u4e00-\u9fff]', text))
|
||||
|
||||
def extract_workflows_section(file_path: Path) -> dict:
|
||||
"""从 .ts 文件中提取 workflows 部分的键值对"""
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# 找到 workflows 部分
|
||||
workflows_match = re.search(r'workflows:\s*\{(.*?)\n\s*\},?\s*\n', content, re.DOTALL)
|
||||
if not workflows_match:
|
||||
return {}
|
||||
|
||||
workflows_content = workflows_match.group(1)
|
||||
|
||||
# 提取所有键值对
|
||||
pattern = r"(\w+):\s*['\"]([^'\"]*(?:\\.[^'\"]*)*)['\"]"
|
||||
matches = re.findall(pattern, workflows_content)
|
||||
|
||||
result = {}
|
||||
for key, value in matches:
|
||||
# 处理转义字符
|
||||
value = value.replace("\\'", "'").replace('\\"', '"')
|
||||
result[key] = value
|
||||
|
||||
return result
|
||||
|
||||
def translate_text(client: Anthropic, text: str, target_lang: str = "Russian") -> str:
|
||||
"""使用 Claude API 翻译文本"""
|
||||
try:
|
||||
message = client.messages.create(
|
||||
model="claude-3-5-sonnet-20241022",
|
||||
max_tokens=1000,
|
||||
messages=[{
|
||||
"role": "user",
|
||||
"content": f"""Translate the following English text to {target_lang}.
|
||||
Only provide the translation, no explanations or additional text.
|
||||
Keep any {{variable}} placeholders unchanged.
|
||||
|
||||
Text to translate: {text}"""
|
||||
}]
|
||||
)
|
||||
|
||||
translation = message.content[0].text.strip()
|
||||
# 移除可能的引号
|
||||
translation = translation.strip('"').strip("'")
|
||||
return translation
|
||||
except Exception as e:
|
||||
print(f"翻译错误: {e}")
|
||||
return text
|
||||
|
||||
def main():
|
||||
# 检查 API key
|
||||
api_key = os.getenv('ANTHROPIC_API_KEY')
|
||||
if not api_key:
|
||||
print("错误: 未找到 ANTHROPIC_API_KEY 环境变量")
|
||||
print("请设置: export ANTHROPIC_API_KEY='your-api-key'")
|
||||
return
|
||||
|
||||
client = Anthropic(api_key=api_key)
|
||||
|
||||
base_dir = Path(__file__).parent
|
||||
en_us_file = base_dir / "src/i18n/locales/en-US.ts"
|
||||
ru_ru_file = base_dir / "src/i18n/locales/ru-RU.ts"
|
||||
|
||||
# 提取英文和俄文的 workflows 部分
|
||||
print("正在提取英文原文...")
|
||||
en_workflows = extract_workflows_section(en_us_file)
|
||||
print(f"提取了 {len(en_workflows)} 个英文键")
|
||||
|
||||
print("\n正在提取俄文翻译...")
|
||||
ru_workflows = extract_workflows_section(ru_ru_file)
|
||||
print(f"提取了 {len(ru_workflows)} 个俄文键")
|
||||
|
||||
# 找出包含中文的俄文翻译
|
||||
problematic_keys = []
|
||||
for key, ru_text in ru_workflows.items():
|
||||
if contains_chinese(ru_text):
|
||||
en_text = en_workflows.get(key, '')
|
||||
if en_text:
|
||||
problematic_keys.append({
|
||||
'key': key,
|
||||
'en_text': en_text,
|
||||
'ru_text': ru_text
|
||||
})
|
||||
|
||||
print(f"\n发现 {len(problematic_keys)} 个需要修复的翻译")
|
||||
|
||||
if not problematic_keys:
|
||||
print("没有需要修复的翻译!")
|
||||
return
|
||||
|
||||
# 翻译前10个作为示例
|
||||
print("\n开始翻译前10个键...")
|
||||
fixed_translations = {}
|
||||
|
||||
for i, item in enumerate(problematic_keys[:10], 1):
|
||||
key = item['key']
|
||||
en_text = item['en_text']
|
||||
old_ru_text = item['ru_text']
|
||||
|
||||
print(f"\n[{i}/10] 翻译键: {key}")
|
||||
print(f" 英文: {en_text}")
|
||||
print(f" 旧俄文: {old_ru_text}")
|
||||
|
||||
new_ru_text = translate_text(client, en_text, "Russian")
|
||||
print(f" 新俄文: {new_ru_text}")
|
||||
|
||||
fixed_translations[key] = new_ru_text
|
||||
|
||||
# 保存修复结果到文件
|
||||
output_file = base_dir / "russian_fixes.txt"
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
f.write(f"需要修复的翻译总数: {len(problematic_keys)}\n\n")
|
||||
f.write("=" * 80 + "\n")
|
||||
f.write("已翻译的前10个键:\n")
|
||||
f.write("=" * 80 + "\n\n")
|
||||
|
||||
for key, new_text in fixed_translations.items():
|
||||
old_item = next(item for item in problematic_keys if item['key'] == key)
|
||||
f.write(f"键: {key}\n")
|
||||
f.write(f"英文: {old_item['en_text']}\n")
|
||||
f.write(f"旧俄文: {old_item['ru_text']}\n")
|
||||
f.write(f"新俄文: {new_text}\n")
|
||||
f.write("-" * 80 + "\n\n")
|
||||
|
||||
f.write("\n" + "=" * 80 + "\n")
|
||||
f.write("剩余需要翻译的键:\n")
|
||||
f.write("=" * 80 + "\n\n")
|
||||
|
||||
for item in problematic_keys[10:]:
|
||||
f.write(f"键: {item['key']}\n")
|
||||
f.write(f"英文: {item['en_text']}\n")
|
||||
f.write(f"旧俄文: {item['ru_text']}\n")
|
||||
f.write("-" * 80 + "\n\n")
|
||||
|
||||
print(f"\n修复结果已保存到: {output_file}")
|
||||
print(f"\n注意: 由于翻译数量较多({len(problematic_keys)}个),建议分批处理")
|
||||
print("您可以修改脚本中的 [:10] 来处理更多键")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
250
web/replace_ru_workflows.py
Normal file
250
web/replace_ru_workflows.py
Normal file
@@ -0,0 +1,250 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
从 en-US.ts 提取 workflows 部分,生成干净的俄语翻译
|
||||
策略:直接替换整个 workflows 部分
|
||||
"""
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
def extract_workflows_section(file_path: Path) -> tuple[str, int, int]:
|
||||
"""提取 workflows 部分的内容和行号范围"""
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
start_line = None
|
||||
end_line = None
|
||||
brace_count = 0
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
if 'workflows:' in line and '{' in line:
|
||||
start_line = i
|
||||
brace_count = line.count('{') - line.count('}')
|
||||
continue
|
||||
|
||||
if start_line is not None:
|
||||
brace_count += line.count('{') - line.count('}')
|
||||
if brace_count == 0:
|
||||
end_line = i
|
||||
break
|
||||
|
||||
if start_line is not None and end_line is not None:
|
||||
section = ''.join(lines[start_line:end_line+1])
|
||||
return section, start_line, end_line
|
||||
|
||||
return '', -1, -1
|
||||
|
||||
def translate_en_to_ru(text: str) -> str:
|
||||
"""简单的英译俄映射"""
|
||||
translations = {
|
||||
# 基础词汇
|
||||
'Workflow': 'Рабочий процесс',
|
||||
'workflow': 'рабочий процесс',
|
||||
'Node': 'Узел',
|
||||
'node': 'узел',
|
||||
'nodes': 'узлы',
|
||||
'Nodes': 'Узлы',
|
||||
'Success': 'Успех',
|
||||
'success': 'успех',
|
||||
'Failed': 'Не удалось',
|
||||
'failed': 'не удалось',
|
||||
'Error': 'Ошибка',
|
||||
'error': 'ошибка',
|
||||
'Loading': 'Загрузка',
|
||||
'loading': 'загрузка',
|
||||
'Save': 'Сохранить',
|
||||
'save': 'сохранить',
|
||||
'Delete': 'Удалить',
|
||||
'delete': 'удалить',
|
||||
'Edit': 'Редактировать',
|
||||
'edit': 'редактировать',
|
||||
'Create': 'Создать',
|
||||
'create': 'создать',
|
||||
'Copy': 'Копировать',
|
||||
'copy': 'копировать',
|
||||
'Paste': 'Вставить',
|
||||
'paste': 'вставить',
|
||||
'Copied': 'Скопировано',
|
||||
'copied': 'скопировано',
|
||||
'Pasted': 'Вставлено',
|
||||
'pasted': 'вставлено',
|
||||
'Configuration': 'Конфигурация',
|
||||
'configuration': 'конфигурация',
|
||||
'Execution': 'Выполнение',
|
||||
'execution': 'выполнение',
|
||||
'executions': 'выполнения',
|
||||
'Executions': 'Выполнения',
|
||||
'Record': 'Запись',
|
||||
'record': 'запись',
|
||||
'records': 'записи',
|
||||
'Records': 'Записи',
|
||||
'Status': 'Статус',
|
||||
'status': 'статус',
|
||||
'All': 'Все',
|
||||
'all': 'все',
|
||||
'Duration': 'Продолжительность',
|
||||
'duration': 'продолжительность',
|
||||
'Time': 'Время',
|
||||
'time': 'время',
|
||||
'Rate': 'Коэффициент',
|
||||
'rate': 'коэффициент',
|
||||
'Average': 'Средний',
|
||||
'average': 'средний',
|
||||
'Total': 'Всего',
|
||||
'total': 'всего',
|
||||
'Last': 'Последний',
|
||||
'last': 'последний',
|
||||
'Condition': 'Условие',
|
||||
'condition': 'условие',
|
||||
'Branch': 'Ветвь',
|
||||
'branch': 'ветвь',
|
||||
'Variable': 'Переменная',
|
||||
'variable': 'переменная',
|
||||
'Context': 'Контекст',
|
||||
'context': 'контекст',
|
||||
'Debug': 'Отладка',
|
||||
'debug': 'отладка',
|
||||
'Message': 'Сообщение',
|
||||
'message': 'сообщение',
|
||||
'Request': 'Запрос',
|
||||
'request': 'запрос',
|
||||
'Body': 'Тело',
|
||||
'body': 'тело',
|
||||
'Empty': 'Пусто',
|
||||
'empty': 'пусто',
|
||||
'Clipboard': 'Буфер обмена',
|
||||
'clipboard': 'буфер обмена',
|
||||
'Search': 'Поиск',
|
||||
'search': 'поиск',
|
||||
'Type': 'Тип',
|
||||
'type': 'тип',
|
||||
'Found': 'Найдено',
|
||||
'found': 'найдено',
|
||||
'Drag': 'Перетащить',
|
||||
'drag': 'перетащить',
|
||||
'Canvas': 'Холст',
|
||||
'canvas': 'холст',
|
||||
'Select': 'Выбрать',
|
||||
'select': 'выбрать',
|
||||
'Edge': 'Соединение',
|
||||
'edge': 'соединение',
|
||||
'Click': 'Нажать',
|
||||
'click': 'нажать',
|
||||
'View': 'Просмотреть',
|
||||
'view': 'просмотреть',
|
||||
'Properties': 'Свойства',
|
||||
'properties': 'свойства',
|
||||
'Configured': 'Настроено',
|
||||
'configured': 'настроено',
|
||||
'Expression': 'Выражение',
|
||||
'expression': 'выражение',
|
||||
'Enter': 'Введите',
|
||||
'enter': 'введите',
|
||||
'When': 'Когда',
|
||||
'when': 'когда',
|
||||
'Will': 'Будет',
|
||||
'will': 'будет',
|
||||
'Always': 'Всегда',
|
||||
'always': 'всегда',
|
||||
'Executed': 'Выполнено',
|
||||
'executed': 'выполнено',
|
||||
'Support': 'Поддержка',
|
||||
'support': 'поддержка',
|
||||
'Reference': 'Ссылка',
|
||||
'reference': 'ссылка',
|
||||
'Confirm': 'Подтвердить',
|
||||
'confirm': 'подтвердить',
|
||||
'After': 'После',
|
||||
'after': 'после',
|
||||
'Removed': 'Удалено',
|
||||
'removed': 'удалено',
|
||||
'Permanently': 'Навсегда',
|
||||
'permanently': 'навсегда',
|
||||
'Label': 'Метка',
|
||||
'label': 'метка',
|
||||
'Display': 'Отображение',
|
||||
'display': 'отображение',
|
||||
'Name': 'Имя',
|
||||
'name': 'имя',
|
||||
'Simulate': 'Имитировать',
|
||||
'simulate': 'имитировать',
|
||||
'Options': 'Параметры',
|
||||
'options': 'параметры',
|
||||
'No': 'Нет',
|
||||
'no': 'нет',
|
||||
'Unsaved': 'Несохраненные',
|
||||
'unsaved': 'несохраненные',
|
||||
'Changes': 'Изменения',
|
||||
'changes': 'изменения',
|
||||
'Nothing': 'Ничего',
|
||||
'nothing': 'ничего',
|
||||
'Selected': 'Выбрано',
|
||||
'selected': 'выбрано',
|
||||
'Matching': 'Совпадающие',
|
||||
'matching': 'совпадающие',
|
||||
'Add': 'Добавить',
|
||||
'add': 'добавить',
|
||||
'Connection': 'Соединение',
|
||||
'connection': 'соединение',
|
||||
}
|
||||
|
||||
result = text
|
||||
for en, ru in translations.items():
|
||||
result = result.replace(f"'{en}'", f"'{ru}'")
|
||||
result = result.replace(f'"{en}"', f'"{ru}"')
|
||||
|
||||
return result
|
||||
|
||||
def main():
|
||||
base_dir = Path(__file__).parent
|
||||
en_us_file = base_dir / "src/i18n/locales/en-US.ts"
|
||||
ru_ru_file = base_dir / "src/i18n/locales/ru-RU.ts"
|
||||
|
||||
print("📖 从 en-US.ts 提取 workflows 部分...")
|
||||
en_workflows, en_start, en_end = extract_workflows_section(en_us_file)
|
||||
|
||||
if not en_workflows:
|
||||
print("❌ 无法提取 en-US.ts 的 workflows 部分")
|
||||
return
|
||||
|
||||
print(f"✓ 提取了 {en_end - en_start + 1} 行")
|
||||
|
||||
print("\n🔄 翻译成俄语...")
|
||||
ru_workflows = translate_en_to_ru(en_workflows)
|
||||
|
||||
print("\n📝 读取 ru-RU.ts 文件...")
|
||||
with open(ru_ru_file, 'r', encoding='utf-8') as f:
|
||||
ru_lines = f.readlines()
|
||||
|
||||
# 找到 ru-RU.ts 中的 workflows 部分
|
||||
print("🔍 定位 ru-RU.ts 中的 workflows 部分...")
|
||||
_, ru_start, ru_end = extract_workflows_section(ru_ru_file)
|
||||
|
||||
if ru_start == -1:
|
||||
print("❌ 无法找到 ru-RU.ts 的 workflows 部分")
|
||||
return
|
||||
|
||||
print(f"✓ 找到位置: 行 {ru_start+1} 到 {ru_end+1}")
|
||||
|
||||
# 替换
|
||||
print("\n✏️ 替换 workflows 部分...")
|
||||
new_lines = ru_lines[:ru_start] + [ru_workflows] + ru_lines[ru_end+1:]
|
||||
|
||||
# 写回文件
|
||||
with open(ru_ru_file, 'w', encoding='utf-8') as f:
|
||||
f.writelines(new_lines)
|
||||
|
||||
print(f"✅ 已更新 {ru_ru_file}")
|
||||
|
||||
# 验证
|
||||
print("\n🔍 验证结果...")
|
||||
with open(ru_ru_file, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
chinese_count = len(re.findall(r'[\u4e00-\u9fff]', content))
|
||||
if chinese_count == 0:
|
||||
print("✅ 验证通过:没有中文字符!")
|
||||
else:
|
||||
print(f"⚠️ 仍有 {chinese_count} 个中文字符")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
PALETTE_CATEGORY_BORDER as categoryBorderColors,
|
||||
CATEGORY_ICONS as categoryIcons,
|
||||
findNodeI18nKeys,
|
||||
getIconComponent,
|
||||
} from './workflow-constants';
|
||||
import { resolveI18nLabel } from './workflow-i18n';
|
||||
|
||||
@@ -35,6 +36,7 @@ const categoryI18nKeys = CATEGORY_I18N_KEYS;
|
||||
interface NodeTypeForUI {
|
||||
type: string;
|
||||
category: string;
|
||||
icon?: string; // Lucide icon name from backend
|
||||
labelKey?: string;
|
||||
descriptionKey?: string;
|
||||
// Also support raw label dict from backend
|
||||
@@ -305,7 +307,7 @@ export default function NodePalette() {
|
||||
{isExpanded && (
|
||||
<div className="mt-1 space-y-0.5 ml-2">
|
||||
{nodes.map((node) => {
|
||||
const Icon = nodeIcons[node.type] || Settings;
|
||||
const Icon = getIconComponent(node.icon, node.type);
|
||||
const label = getNodeLabel(node);
|
||||
const description = getNodeDescription(node);
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
NODE_ICONS,
|
||||
NODE_TYPE_I18N_KEYS,
|
||||
getNodeTypeLabel,
|
||||
getIconComponent,
|
||||
} from './workflow-constants';
|
||||
import { resolveI18nLabel, maybeTranslateKey } from './workflow-i18n';
|
||||
import type { I18nObject } from '@/app/infra/entities/common';
|
||||
@@ -143,6 +144,7 @@ const statusConfig: Record<
|
||||
export interface WorkflowNodeData extends Record<string, unknown> {
|
||||
label: string;
|
||||
type: string;
|
||||
icon?: string; // Lucide icon name from backend
|
||||
config: Record<string, unknown>;
|
||||
inputs?: {
|
||||
name: string;
|
||||
@@ -214,7 +216,7 @@ function WorkflowNodeComponent({ data, selected }: NodeProps) {
|
||||
const nodeData = data as WorkflowNodeData;
|
||||
const category = nodeData.type.split('.')[0];
|
||||
const colors = categoryColors[category] || categoryColors.process;
|
||||
const Icon = nodeIcons[nodeData.type] || Settings;
|
||||
const Icon = getIconComponent(nodeData.icon as string | undefined, nodeData.type);
|
||||
|
||||
// Get execution status
|
||||
const status = nodeData.executionStatus || 'idle';
|
||||
|
||||
@@ -236,6 +236,16 @@ export const CATEGORY_I18N_KEYS: Record<string, { labelKey: string }> = {
|
||||
|
||||
// ─── Icon mapping ───────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Legacy hardcoded icon mapping for workflow nodes.
|
||||
*
|
||||
* @deprecated This mapping is kept for backward compatibility only.
|
||||
* New code should use the dynamic icon resolution via getIconComponent(),
|
||||
* which reads icon names from the backend node definitions.
|
||||
*
|
||||
* This will be removed in a future version once all nodes properly
|
||||
* define their icons in the backend.
|
||||
*/
|
||||
export const NODE_ICONS: Record<string, React.ElementType> = {
|
||||
// Trigger
|
||||
'trigger.message': MessageSquare,
|
||||
@@ -411,3 +421,51 @@ export function getNodeTypeLabel(
|
||||
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
// ─── Dynamic Icon Resolution ────────────────────────────────────────
|
||||
|
||||
import * as LucideIcons from 'lucide-react';
|
||||
|
||||
/**
|
||||
* Dynamically get Lucide icon component from backend icon name.
|
||||
*
|
||||
* This function enables the frontend to use icon names provided by the backend,
|
||||
* eliminating the need to maintain a hardcoded NODE_ICONS mapping.
|
||||
*
|
||||
* @param iconName - Lucide icon name from backend (e.g., 'MessageSquare', 'Brain')
|
||||
* @param nodeType - Node type for fallback to hardcoded mapping (backward compatibility)
|
||||
* @returns React component for the icon
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* const Icon = getIconComponent('MessageSquare');
|
||||
* return <Icon className="size-5" />;
|
||||
* ```
|
||||
*/
|
||||
export function getIconComponent(
|
||||
iconName: string | undefined,
|
||||
nodeType?: string
|
||||
): React.ElementType {
|
||||
// 1. Priority: Use backend-provided icon name
|
||||
if (iconName) {
|
||||
const IconComponent = (LucideIcons as any)[iconName];
|
||||
if (IconComponent && typeof IconComponent === 'function') {
|
||||
return IconComponent;
|
||||
}
|
||||
// Warn if icon name is invalid
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.warn(
|
||||
`[Workflow] Icon "${iconName}" not found in Lucide icons. ` +
|
||||
`Falling back to default. Check: https://lucide.dev/icons/`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Fallback: Use hardcoded NODE_ICONS mapping (backward compatibility)
|
||||
if (nodeType && NODE_ICONS[nodeType]) {
|
||||
return NODE_ICONS[nodeType];
|
||||
}
|
||||
|
||||
// 3. Final fallback: Default Settings icon
|
||||
return LucideIcons.Settings;
|
||||
}
|
||||
|
||||
@@ -1520,6 +1520,9 @@ const enUS = {
|
||||
question: 'Question',
|
||||
parameters: 'Parameters',
|
||||
key: 'Key',
|
||||
payload: 'Payload',
|
||||
input_value: 'Input Value',
|
||||
conversation_id: 'Conversation ID',
|
||||
// Control inputs
|
||||
case_1: 'Case 1',
|
||||
case_2: 'Case 2',
|
||||
@@ -1982,16 +1985,27 @@ const enUS = {
|
||||
},
|
||||
// Node outputs i18n
|
||||
nodeOutputs: {
|
||||
// ===== Common Outputs =====
|
||||
output: 'Output',
|
||||
result: 'Result',
|
||||
data: 'Data',
|
||||
error: 'Error',
|
||||
success: 'Success',
|
||||
|
||||
// ===== Trigger Nodes =====
|
||||
// trigger.py - MessageTriggerNode
|
||||
message: 'Message Content',
|
||||
sender: 'Sender',
|
||||
sender_id: 'Sender ID',
|
||||
sender_name: 'Sender Name',
|
||||
platform: 'Platform',
|
||||
conversation_id: 'Conversation ID',
|
||||
is_group: 'Is Group Chat',
|
||||
context: 'Full Message Context',
|
||||
event: 'Event',
|
||||
// trigger.py - CronTriggerNode
|
||||
timestamp: 'Trigger Time',
|
||||
trigger_time: 'Trigger Time',
|
||||
schedule: 'Trigger Schedule',
|
||||
cron_timestamp: 'Cron Timestamp',
|
||||
cron_schedule: 'Cron Schedule',
|
||||
@@ -2000,6 +2014,7 @@ const enUS = {
|
||||
body: 'Request Body',
|
||||
headers: 'Request Headers',
|
||||
query: 'Query Parameters',
|
||||
query_params: 'Query Parameters',
|
||||
method: 'Request Method',
|
||||
webhook_body: 'Webhook Body',
|
||||
webhook_headers: 'Webhook Headers',
|
||||
@@ -2015,10 +2030,10 @@ const enUS = {
|
||||
prompt: 'Prompt/Question',
|
||||
context_info: 'Context Information',
|
||||
// process.py - CodeExecutorNode
|
||||
output: 'Output Data',
|
||||
console: 'Console Output',
|
||||
code_input: 'Code Input',
|
||||
code_output: 'Code Output',
|
||||
logs: 'Logs',
|
||||
// process.py - HTTPRequestNode
|
||||
status_code: 'Status Code',
|
||||
http_body: 'HTTP Body',
|
||||
@@ -2026,23 +2041,27 @@ const enUS = {
|
||||
http_response: 'HTTP Response',
|
||||
response_headers: 'Response Headers',
|
||||
// process.py - DataTransformNode
|
||||
result: 'Transform Result',
|
||||
transform_input: 'Transform Input',
|
||||
transform_result: 'Transform Result',
|
||||
// process.py - QuestionClassifierNode
|
||||
category: 'Category Result',
|
||||
confidence: 'Confidence',
|
||||
all_scores: 'All Category Scores',
|
||||
scores: 'Scores',
|
||||
question: 'User Question',
|
||||
// process.py - ParameterExtractorNode
|
||||
parameters: 'Extracted Parameters',
|
||||
extraction_success: 'Extraction Success',
|
||||
extract_text: 'Input Text',
|
||||
missing: 'Missing Parameters',
|
||||
parsed: 'Parsed Result',
|
||||
// process.py - KnowledgeRetrievalNode
|
||||
documents: 'Retrieved Documents',
|
||||
citations: 'Citation Information',
|
||||
knowledge_context: 'Merged Context',
|
||||
knowledge_query: 'Retrieval Query',
|
||||
chunks: 'Text Chunks',
|
||||
text: 'Text',
|
||||
// control.py - ConditionNode
|
||||
true: 'True Branch Output',
|
||||
false: 'False Branch Output',
|
||||
@@ -2051,12 +2070,15 @@ const enUS = {
|
||||
matched_case: 'Matched Branch Output',
|
||||
default: 'Default Branch Output',
|
||||
switch_input: 'Switch Input',
|
||||
case_1: 'Case 1',
|
||||
case_2: 'Case 2',
|
||||
// control.py - LoopNode
|
||||
item: 'Current Item',
|
||||
index: 'Current Index',
|
||||
results: 'All Iteration Results',
|
||||
completed: 'Is Completed',
|
||||
loop_items: 'Items to Iterate',
|
||||
count: 'Count',
|
||||
// control.py - IteratorNode
|
||||
is_first: 'Is First',
|
||||
is_last: 'Is Last',
|
||||
@@ -2077,6 +2099,8 @@ const enUS = {
|
||||
merge_input_2: 'Input 2',
|
||||
merge_input_3: 'Input 3',
|
||||
merge_input_4: 'Input 4',
|
||||
branch_1: 'Branch 1 Output',
|
||||
branch_2: 'Branch 2 Output',
|
||||
// control.py - VariableAggregatorNode
|
||||
aggregated: 'Aggregated Variables',
|
||||
aggregator_variables: 'Variable Input',
|
||||
@@ -2084,6 +2108,8 @@ const enUS = {
|
||||
status: 'Send Status',
|
||||
message_id: 'Message ID',
|
||||
target: 'Target ID',
|
||||
execution_id: 'Execution ID',
|
||||
notification_id: 'Notification ID',
|
||||
// action.py - ReplyMessageNode
|
||||
reply_message: 'Reply Content',
|
||||
// action.py - CallPipelineNode
|
||||
@@ -2101,6 +2127,7 @@ const enUS = {
|
||||
// action.py - OpeningStatementNode
|
||||
statement: 'Opening Statement',
|
||||
suggested_questions: 'Suggested Questions',
|
||||
suggestions: 'Suggestions',
|
||||
// action.py - EndNode
|
||||
workflow_output: 'Workflow Output',
|
||||
final_result: 'Final Result',
|
||||
@@ -2108,13 +2135,11 @@ const enUS = {
|
||||
query_results: 'Query Results',
|
||||
row_count: 'Affected/Returned Rows',
|
||||
query_success: 'Query Success',
|
||||
query_params: 'Query Parameters',
|
||||
// integration.py - RedisOperationNode
|
||||
redis_result: 'Operation Result',
|
||||
redis_success: 'Operation Success',
|
||||
redis_key: 'Redis Key',
|
||||
redis_value: 'Redis Value',
|
||||
error: 'Error Message',
|
||||
plugin_input: 'Plugin Input',
|
||||
// integration.py - MCPToolNode
|
||||
tool_result: 'Tool Execution Result',
|
||||
@@ -2146,6 +2171,12 @@ const enUS = {
|
||||
coze_query: 'User Input/Query',
|
||||
coze_conversation_id: 'Conversation ID',
|
||||
bot_conversation_id: 'Conversation ID',
|
||||
|
||||
// ===== Additional Keys =====
|
||||
embedding: 'Embedding',
|
||||
dimensions: 'Dimensions',
|
||||
intent: 'Intent',
|
||||
entities: 'Entities',
|
||||
},
|
||||
},
|
||||
unifiedBinding: {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1450
web/todo_translations.json
Normal file
1450
web/todo_translations.json
Normal file
File diff suppressed because it is too large
Load Diff
265
web/translate_i18n_keys.py
Normal file
265
web/translate_i18n_keys.py
Normal file
@@ -0,0 +1,265 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
将英文翻译键翻译成对应的目标语言
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
||||
# 翻译映射表
|
||||
TRANSLATIONS = {
|
||||
'ja-JP': { # 日文
|
||||
'Sender': '送信者',
|
||||
'Output': '出力',
|
||||
'Result': '結果',
|
||||
'Data': 'データ',
|
||||
'Error Message': 'エラーメッセージ',
|
||||
'Success Status': '成功ステータス',
|
||||
'Event': 'イベント',
|
||||
'Trigger Time': 'トリガー時刻',
|
||||
'Logs': 'ログ',
|
||||
'Scores': 'スコア',
|
||||
'Missing Parameters': '不足パラメータ',
|
||||
'Parsed Result': '解析結果',
|
||||
'Text Chunks': 'テキストチャンク',
|
||||
'Text Content': 'テキスト内容',
|
||||
'Case 1 Output': 'ケース1出力',
|
||||
'Case 2 Output': 'ケース2出力',
|
||||
'Branch 1 Output': 'ブランチ1出力',
|
||||
'Branch 2 Output': 'ブランチ2出力',
|
||||
'Count': 'カウント',
|
||||
'Execution ID': '実行ID',
|
||||
'Notification ID': '通知ID',
|
||||
'Suggestions': '提案',
|
||||
'Embedding Vector': '埋め込みベクトル',
|
||||
'Vector Dimensions': 'ベクトル次元',
|
||||
'Intent': '意図',
|
||||
'Entities': 'エンティティ',
|
||||
'Payload': 'ペイロード',
|
||||
'Input Value': '入力値',
|
||||
'Conversation ID': '会話ID',
|
||||
},
|
||||
'zh-Hant': { # 繁体中文
|
||||
'Sender': '發送者',
|
||||
'Output': '輸出',
|
||||
'Result': '結果',
|
||||
'Data': '數據',
|
||||
'Error Message': '錯誤訊息',
|
||||
'Success Status': '成功狀態',
|
||||
'Event': '事件',
|
||||
'Trigger Time': '觸發時間',
|
||||
'Logs': '日誌',
|
||||
'Scores': '分數',
|
||||
'Missing Parameters': '缺失參數',
|
||||
'Parsed Result': '解析結果',
|
||||
'Text Chunks': '文本塊',
|
||||
'Text Content': '文本內容',
|
||||
'Case 1 Output': '情況1輸出',
|
||||
'Case 2 Output': '情況2輸出',
|
||||
'Branch 1 Output': '分支1輸出',
|
||||
'Branch 2 Output': '分支2輸出',
|
||||
'Count': '計數',
|
||||
'Execution ID': '執行ID',
|
||||
'Notification ID': '通知ID',
|
||||
'Suggestions': '建議',
|
||||
'Embedding Vector': '嵌入向量',
|
||||
'Vector Dimensions': '向量維度',
|
||||
'Intent': '意圖',
|
||||
'Entities': '實體',
|
||||
'Payload': '負載',
|
||||
'Input Value': '輸入值',
|
||||
'Conversation ID': '對話ID',
|
||||
},
|
||||
'es-ES': { # 西班牙语
|
||||
'Sender': 'Remitente',
|
||||
'Output': 'Salida',
|
||||
'Result': 'Resultado',
|
||||
'Data': 'Datos',
|
||||
'Error Message': 'Mensaje de Error',
|
||||
'Success Status': 'Estado de Éxito',
|
||||
'Event': 'Evento',
|
||||
'Trigger Time': 'Hora de Activación',
|
||||
'Logs': 'Registros',
|
||||
'Scores': 'Puntuaciones',
|
||||
'Missing Parameters': 'Parámetros Faltantes',
|
||||
'Parsed Result': 'Resultado Analizado',
|
||||
'Text Chunks': 'Fragmentos de Texto',
|
||||
'Text Content': 'Contenido de Texto',
|
||||
'Case 1 Output': 'Salida Caso 1',
|
||||
'Case 2 Output': 'Salida Caso 2',
|
||||
'Branch 1 Output': 'Salida Rama 1',
|
||||
'Branch 2 Output': 'Salida Rama 2',
|
||||
'Count': 'Conteo',
|
||||
'Execution ID': 'ID de Ejecución',
|
||||
'Notification ID': 'ID de Notificación',
|
||||
'Suggestions': 'Sugerencias',
|
||||
'Embedding Vector': 'Vector de Incrustación',
|
||||
'Vector Dimensions': 'Dimensiones del Vector',
|
||||
'Intent': 'Intención',
|
||||
'Entities': 'Entidades',
|
||||
'Payload': 'Carga Útil',
|
||||
'Input Value': 'Valor de Entrada',
|
||||
'Conversation ID': 'ID de Conversación',
|
||||
},
|
||||
'ru-RU': { # 俄语
|
||||
'Sender': 'Отправитель',
|
||||
'Output': 'Вывод',
|
||||
'Result': 'Результат',
|
||||
'Data': 'Данные',
|
||||
'Error Message': 'Сообщение об Ошибке',
|
||||
'Success Status': 'Статус Успеха',
|
||||
'Event': 'Событие',
|
||||
'Trigger Time': 'Время Триггера',
|
||||
'Logs': 'Журналы',
|
||||
'Scores': 'Оценки',
|
||||
'Missing Parameters': 'Отсутствующие Параметры',
|
||||
'Parsed Result': 'Разобранный Результат',
|
||||
'Text Chunks': 'Фрагменты Текста',
|
||||
'Text Content': 'Текстовое Содержимое',
|
||||
'Case 1 Output': 'Вывод Случая 1',
|
||||
'Case 2 Output': 'Вывод Случая 2',
|
||||
'Branch 1 Output': 'Вывод Ветви 1',
|
||||
'Branch 2 Output': 'Вывод Ветви 2',
|
||||
'Count': 'Количество',
|
||||
'Execution ID': 'ID Выполнения',
|
||||
'Notification ID': 'ID Уведомления',
|
||||
'Suggestions': 'Предложения',
|
||||
'Embedding Vector': 'Вектор Встраивания',
|
||||
'Vector Dimensions': 'Размерности Вектора',
|
||||
'Intent': 'Намерение',
|
||||
'Entities': 'Сущности',
|
||||
'Payload': 'Полезная Нагрузка',
|
||||
'Input Value': 'Входное Значение',
|
||||
'Conversation ID': 'ID Разговора',
|
||||
},
|
||||
'th-TH': { # 泰语
|
||||
'Sender': 'ผู้ส่ง',
|
||||
'Output': 'ผลลัพธ์',
|
||||
'Result': 'ผลลัพธ์',
|
||||
'Data': 'ข้อมูล',
|
||||
'Error Message': 'ข้อความข้อผิดพลาด',
|
||||
'Success Status': 'สถานะความสำเร็จ',
|
||||
'Event': 'เหตุการณ์',
|
||||
'Trigger Time': 'เวลาทริกเกอร์',
|
||||
'Logs': 'บันทึก',
|
||||
'Scores': 'คะแนน',
|
||||
'Missing Parameters': 'พารามิเตอร์ที่ขาดหายไป',
|
||||
'Parsed Result': 'ผลการแยกวิเคราะห์',
|
||||
'Text Chunks': 'ส่วนข้อความ',
|
||||
'Text Content': 'เนื้อหาข้อความ',
|
||||
'Case 1 Output': 'ผลลัพธ์กรณีที่ 1',
|
||||
'Case 2 Output': 'ผลลัพธ์กรณีที่ 2',
|
||||
'Branch 1 Output': 'ผลลัพธ์สาขา 1',
|
||||
'Branch 2 Output': 'ผลลัพธ์สาขา 2',
|
||||
'Count': 'จำนวน',
|
||||
'Execution ID': 'ID การดำเนินการ',
|
||||
'Notification ID': 'ID การแจ้งเตือน',
|
||||
'Suggestions': 'คำแนะนำ',
|
||||
'Embedding Vector': 'เวกเตอร์ฝังตัว',
|
||||
'Vector Dimensions': 'มิติเวกเตอร์',
|
||||
'Intent': 'เจตนา',
|
||||
'Entities': 'เอนทิตี',
|
||||
'Payload': 'เพย์โหลด',
|
||||
'Input Value': 'ค่าอินพุต',
|
||||
'Conversation ID': 'ID การสนทนา',
|
||||
},
|
||||
'vi-VN': { # 越南语
|
||||
'Sender': 'Người gửi',
|
||||
'Output': 'Đầu ra',
|
||||
'Result': 'Kết quả',
|
||||
'Data': 'Dữ liệu',
|
||||
'Error Message': 'Thông báo Lỗi',
|
||||
'Success Status': 'Trạng thái Thành công',
|
||||
'Event': 'Sự kiện',
|
||||
'Trigger Time': 'Thời gian Kích hoạt',
|
||||
'Logs': 'Nhật ký',
|
||||
'Scores': 'Điểm số',
|
||||
'Missing Parameters': 'Tham số Thiếu',
|
||||
'Parsed Result': 'Kết quả Phân tích',
|
||||
'Text Chunks': 'Đoạn Văn bản',
|
||||
'Text Content': 'Nội dung Văn bản',
|
||||
'Case 1 Output': 'Đầu ra Trường hợp 1',
|
||||
'Case 2 Output': 'Đầu ra Trường hợp 2',
|
||||
'Branch 1 Output': 'Đầu ra Nhánh 1',
|
||||
'Branch 2 Output': 'Đầu ra Nhánh 2',
|
||||
'Count': 'Số lượng',
|
||||
'Execution ID': 'ID Thực thi',
|
||||
'Notification ID': 'ID Thông báo',
|
||||
'Suggestions': 'Gợi ý',
|
||||
'Embedding Vector': 'Vector Nhúng',
|
||||
'Vector Dimensions': 'Kích thước Vector',
|
||||
'Intent': 'Ý định',
|
||||
'Entities': 'Thực thể',
|
||||
'Payload': 'Tải trọng',
|
||||
'Input Value': 'Giá trị Đầu vào',
|
||||
'Conversation ID': 'ID Hội thoại',
|
||||
},
|
||||
}
|
||||
|
||||
def translate_file(file_path: Path, lang_code: str):
|
||||
"""翻译指定语言文件"""
|
||||
print(f"\n处理文件: {file_path.name}")
|
||||
|
||||
if lang_code not in TRANSLATIONS:
|
||||
print(f" ⚠️ 没有 {lang_code} 的翻译映射")
|
||||
return
|
||||
|
||||
translations = TRANSLATIONS[lang_code]
|
||||
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
modified = False
|
||||
translated_count = 0
|
||||
|
||||
# 替换翻译
|
||||
for english, translated in translations.items():
|
||||
# 匹配格式: key: 'English Text',
|
||||
pattern = rf"(\s+\w+:\s+)'{re.escape(english)}',"
|
||||
if re.search(pattern, content):
|
||||
content = re.sub(pattern, rf"\1'{translated}',", content)
|
||||
translated_count += 1
|
||||
modified = True
|
||||
print(f" ✓ 翻译: '{english}' -> '{translated}'")
|
||||
|
||||
if modified:
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
print(f" ✅ 已更新 {file_path.name},共翻译 {translated_count} 个键")
|
||||
else:
|
||||
print(f" - {file_path.name} 无需翻译")
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
locales_dir = Path(__file__).parent / 'src' / 'i18n' / 'locales'
|
||||
|
||||
print("=" * 60)
|
||||
print("开始翻译多语言 i18n 文件")
|
||||
print("=" * 60)
|
||||
|
||||
language_files = {
|
||||
'ja-JP.ts': 'ja-JP',
|
||||
'zh-Hant.ts': 'zh-Hant',
|
||||
'es-ES.ts': 'es-ES',
|
||||
'ru-RU.ts': 'ru-RU',
|
||||
'th-TH.ts': 'th-TH',
|
||||
'vi-VN.ts': 'vi-VN',
|
||||
}
|
||||
|
||||
total_translated = 0
|
||||
for filename, lang_code in language_files.items():
|
||||
file_path = locales_dir / filename
|
||||
if file_path.exists():
|
||||
translate_file(file_path, lang_code)
|
||||
total_translated += 1
|
||||
else:
|
||||
print(f"\n警告: 文件不存在 - {filename}")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("翻译完成!")
|
||||
print("=" * 60)
|
||||
print(f"\n已处理 {total_translated} 个语言文件")
|
||||
print(f"每个文件最多翻译 {len(TRANSLATIONS['ja-JP'])} 个键")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
23
web/translation_template.json
Normal file
23
web/translation_template.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"_comment": "翻译模板 - 请填写各语言的翻译",
|
||||
"_instructions": "1. 从workflows_chinese_texts.json复制需要翻译的键; 2. 为每个键添加各语言翻译; 3. 运行apply_translations.py应用翻译",
|
||||
"_languages": {
|
||||
"ja-JP": "日语",
|
||||
"zh-Hant": "繁体中文",
|
||||
"es-ES": "西班牙语",
|
||||
"ru-RU": "俄语",
|
||||
"th-TH": "泰语",
|
||||
"vi-VN": "越南语"
|
||||
},
|
||||
"translations": {
|
||||
"title": {
|
||||
"zh-Hans": "工作流",
|
||||
"ja-JP": "ワークフロー",
|
||||
"zh-Hant": "工作流",
|
||||
"es-ES": "Flujo de trabajo",
|
||||
"ru-RU": "Рабочий процесс",
|
||||
"th-TH": "เวิร์กโฟลว์",
|
||||
"vi-VN": "Quy trình làm việc"
|
||||
}
|
||||
}
|
||||
}
|
||||
629
web/workflows_chinese_texts.json
Normal file
629
web/workflows_chinese_texts.json
Normal file
@@ -0,0 +1,629 @@
|
||||
{
|
||||
"title": "工作流对话",
|
||||
"description": "描述",
|
||||
"createWorkflow": "创建工作流",
|
||||
"selectFromSidebar": "从侧边栏选择一个工作流",
|
||||
"editWorkflow": "编辑工作流",
|
||||
"newWorkflow": "新工作流",
|
||||
"getWorkflowListError": "获取工作流列表失败:",
|
||||
"workflowName": "工作流名称",
|
||||
"workflowDescription": "工作流描述",
|
||||
"workflowNameRequired": "工作流名称不能为空",
|
||||
"defaultDescription": "一个工作流",
|
||||
"getWorkflowError": "获取工作流失败:",
|
||||
"loadError": "加载工作流失败",
|
||||
"saveSuccess": "保存成功",
|
||||
"saveError": "保存失败:",
|
||||
"createSuccess": "工作流创建成功",
|
||||
"createError": "创建失败:",
|
||||
"deleteSuccess": "删除成功",
|
||||
"deleteError": "删除失败:",
|
||||
"deleteConfirmation": "你确定要删除这个工作流吗?",
|
||||
"copySuccess": "复制成功",
|
||||
"copyError": "复制失败:",
|
||||
"export": "导出",
|
||||
"import": "导入",
|
||||
"exportSuccess": "工作流已导出",
|
||||
"importSuccess": "工作流已导入",
|
||||
"importError": "导入失败:文件格式无效",
|
||||
"publish": "发布",
|
||||
"publishSuccess": "发布成功",
|
||||
"publishError": "发布失败",
|
||||
"configuration": "配置",
|
||||
"executions": "执行记录",
|
||||
"editor": "编辑器",
|
||||
"debugChat": "对话调试",
|
||||
"basicInfo": "基础信息",
|
||||
"basicInfoDesc": "设置工作流名称、图标和描述",
|
||||
"basicInfoDescription": "设置工作流名称和描述",
|
||||
"dangerZone": "危险区域",
|
||||
"dangerZoneDesc": "不可逆的操作",
|
||||
"dangerZoneDescription": "不可逆的操作",
|
||||
"deleteWorkflowAction": "删除此工作流",
|
||||
"deleteWorkflowHint": "删除后,所有关联配置将被永久移除,且无法恢复。",
|
||||
"deleteWorkflow": "删除工作流",
|
||||
"deleteConfirm": "确认删除",
|
||||
"deleteConfirmDesc": "您确定要删除工作流 \"{{name}}\" 吗?此操作无法撤销。",
|
||||
"name": "名称",
|
||||
"namePlaceholder": "输入工作流名称",
|
||||
"descriptionPlaceholder": "输入工作流描述(可选)",
|
||||
"enabled": "启用",
|
||||
"enabledDesc": "启用后,工作流将可以被触发执行",
|
||||
"loading": "加载中...",
|
||||
"info": "工作流信息",
|
||||
"uuid": "UUID",
|
||||
"version": "版本",
|
||||
"createdAt": "创建时间",
|
||||
"updatedAt": "更新时间",
|
||||
"totalExecutions": "共 {{count}} 条执行记录",
|
||||
"statistics": "统计分析",
|
||||
"successfulCount": "成功 {{count}} 次",
|
||||
"successRate": "成功率",
|
||||
"averageDuration": "平均耗时",
|
||||
"perExecution": "每次执行",
|
||||
"failedExecutions": "失败次数",
|
||||
"lastExecution": "最后执行",
|
||||
"filterByStatus": "按状态筛选",
|
||||
"allStatuses": "全部状态",
|
||||
"manualTrigger": "手动触发",
|
||||
"executionId": "执行 ID",
|
||||
"status": "状态",
|
||||
"triggerType": "触发类型",
|
||||
"startedAt": "开始时间",
|
||||
"duration": "等待时间",
|
||||
"noExecutions": "暂无执行记录",
|
||||
"executionDetails": "执行详情",
|
||||
"error": "错误",
|
||||
"nodeExecutions": "节点执行记录",
|
||||
"result": "结果",
|
||||
"nodePalette": "节点面板",
|
||||
"properties": "属性",
|
||||
"zoomIn": "放大",
|
||||
"zoomOut": "缩小",
|
||||
"fitView": "适应视图",
|
||||
"unsavedChanges": "有未保存的更改",
|
||||
"paste": "粘贴",
|
||||
"deleted": "已删除",
|
||||
"nothingToCopy": "没有选中的节点可复制",
|
||||
"nothingToPaste": "剪贴板为空",
|
||||
"copied": "已复制 {{count}} 个节点",
|
||||
"pasted": "已粘贴 {{count}} 个节点",
|
||||
"nodesSelected": "已选中 {{count}} 个节点",
|
||||
"edgesSelected": "已选中 {{count}} 条连线",
|
||||
"searchNodes": "搜索节点...",
|
||||
"loadingNodeTypes": "正在加载节点类型...",
|
||||
"noNodesFound": "未找到匹配的节点",
|
||||
"clearSearch": "清除搜索",
|
||||
"dragToAdd": "拖拽节点到画布添加",
|
||||
"selectNodeOrEdge": "选择一个节点或连线",
|
||||
"selectNodeOrEdgeHint": "点击画布中的节点或连线来查看和编辑其属性",
|
||||
"edgeProperties": "连线属性",
|
||||
"nodeProperties": "节点属性",
|
||||
"condition": "条件分支",
|
||||
"hasCondition": "已设置",
|
||||
"conditionPlaceholder": "输入条件表达式,如: output.success == true",
|
||||
"conditionHelp": "条件为空时,该连线将始终被执行。支持使用 {{变量名}} 引用上下文变量。",
|
||||
"deleteEdge": "删除连线",
|
||||
"deleteEdgeConfirm": "确定删除此连线?",
|
||||
"deleteEdgeConfirmDesc": "删除后,该连线将被永久移除。",
|
||||
"nodeLabel": "节点名称",
|
||||
"nodeLabelPlaceholder": "输入节点显示名称",
|
||||
"nodeId": "节点 ID",
|
||||
"inputOutputVariables": "输入/输出变量",
|
||||
"inputs": "输入",
|
||||
"outputs": "输出",
|
||||
"availableVariables": "可用变量",
|
||||
"globalVariables": "全局变量",
|
||||
"messageContent": "模拟消息",
|
||||
"messageSender": "发送者",
|
||||
"platform": "平台",
|
||||
"sessionId": "会话 ID",
|
||||
"timestamp": "时间戳",
|
||||
"nodeConfig": "节点配置",
|
||||
"noConfigOptions": "该节点类型暂无配置选项",
|
||||
"deleteNode": "删除节点",
|
||||
"deleteNodeConfirm": "确定删除此节点?",
|
||||
"deleteNodeConfirmDesc": "删除后,该节点及其所有连线将被永久移除。",
|
||||
"input": "输入",
|
||||
"message": "消息",
|
||||
"text": "文本",
|
||||
"query": "SQL查询",
|
||||
"data": "数据",
|
||||
"value": "值",
|
||||
"content": "内容",
|
||||
"context": "调试上下文",
|
||||
"body": "请求体",
|
||||
"variables": "监控变量",
|
||||
"items": "项目列表",
|
||||
"arguments": "参数",
|
||||
"question": "用户问题",
|
||||
"parameters": "参数定义",
|
||||
"key": "键",
|
||||
"payload": "载荷",
|
||||
"input_value": "输入值",
|
||||
"conversation_id": "会话 ID",
|
||||
"case_1": "分支 1",
|
||||
"case_2": "分支 2",
|
||||
"branch_1": "分支1输出",
|
||||
"branch_2": "分支2输出",
|
||||
"notification_id": "通知ID",
|
||||
"key_template": "键模板",
|
||||
"hash_field": "哈希字段",
|
||||
"server_name": "服务器名称",
|
||||
"tool_name": "工具名称",
|
||||
"arguments_template": "参数模板",
|
||||
"scope": "作用域",
|
||||
"output": "输出",
|
||||
"response": "响应",
|
||||
"sender": "发送者",
|
||||
"sender_id": "发送者 ID",
|
||||
"sender_name": "发送者名称",
|
||||
"cron_timestamp": "Cron 时间戳",
|
||||
"cron_schedule": "Cron 调度表达式",
|
||||
"cron_context": "Cron 上下文",
|
||||
"trigger_time": "触发时间",
|
||||
"schedule": "触发计划",
|
||||
"headers": "请求头",
|
||||
"query_params": "查询参数",
|
||||
"method": "请求方法",
|
||||
"is_group": "是否群聊",
|
||||
"event": "事件",
|
||||
"event_type": "事件类型",
|
||||
"event_data": "事件数据",
|
||||
"event_timestamp": "事件时间戳",
|
||||
"usage": "Token使用统计",
|
||||
"parsed": "解析结果",
|
||||
"category": "分类结果",
|
||||
"confidence": "置信度",
|
||||
"all_scores": "所有分数",
|
||||
"missing": "缺失参数",
|
||||
"success": "是否成功",
|
||||
"scores": "分数",
|
||||
"chunks": "文本块",
|
||||
"count": "数量",
|
||||
"logs": "日志",
|
||||
"embedding": "向量",
|
||||
"dimensions": "维度",
|
||||
"intent": "意图",
|
||||
"entities": "实体",
|
||||
"prompt": "提示/问题",
|
||||
"context_info": "上下文信息",
|
||||
"console": "控制台输出",
|
||||
"code_input": "代码输入",
|
||||
"code_output": "代码输出",
|
||||
"http_body": "HTTP 请求体",
|
||||
"http_headers": "HTTP 请求头",
|
||||
"http_response": "HTTP 响应",
|
||||
"response_headers": "响应头",
|
||||
"transform_input": "转换输入",
|
||||
"transform_result": "转换结果",
|
||||
"extraction_success": "提取成功",
|
||||
"extract_text": "输入文本",
|
||||
"documents": "检索的文档",
|
||||
"citations": "引用信息",
|
||||
"knowledge_context": "合并上下文",
|
||||
"knowledge_query": "检索查询",
|
||||
"true": "条件为真输出",
|
||||
"false": "条件为假输出",
|
||||
"matched_case": "匹配分支",
|
||||
"default": "默认分支",
|
||||
"item": "当前项",
|
||||
"index": "当前索引",
|
||||
"completed": "已完成",
|
||||
"is_first": "是否第一个",
|
||||
"is_last": "是否最后一个",
|
||||
"results": "结果",
|
||||
"condition_input": "条件输入",
|
||||
"switch_input": "开关输入",
|
||||
"loop_items": "迭代项目",
|
||||
"iterator_array": "输入数组",
|
||||
"iterator_item": "当前元素",
|
||||
"iterator_index": "当前索引",
|
||||
"errors": "错误列表",
|
||||
"parallel_input": "并行输入",
|
||||
"parallel_results": "所有分支结果",
|
||||
"wait_input": "透传输入",
|
||||
"wait_output": "透传输出",
|
||||
"merged": "合并结果",
|
||||
"merge_array": "数组结果",
|
||||
"merge_input_1": "输入 1",
|
||||
"merge_input_2": "输入 2",
|
||||
"merge_input_3": "输入 3",
|
||||
"merge_input_4": "输入 4",
|
||||
"aggregated": "聚合变量",
|
||||
"aggregator_variables": "变量输入",
|
||||
"message_id": "消息ID",
|
||||
"status_code": "状态码",
|
||||
"execution_id": "执行ID",
|
||||
"target": "目标 ID",
|
||||
"reply_message": "回复内容",
|
||||
"pipeline_response": "Pipeline 响应",
|
||||
"pipeline_result": "完整结果",
|
||||
"pipeline_query": "查询内容",
|
||||
"context_data": "上下文数据",
|
||||
"store_status": "存储状态",
|
||||
"store_key": "存储键",
|
||||
"store_value": "存储值",
|
||||
"variable_value": "变量值",
|
||||
"variable_result": "设置变量结果",
|
||||
"statement": "开场白",
|
||||
"suggested_questions": "建议问题",
|
||||
"suggestions": "建议问题",
|
||||
"workflow_output": "工作流输出",
|
||||
"final_result": "最终结果",
|
||||
"query_results": "查询结果",
|
||||
"row_count": "行数",
|
||||
"query_success": "查询成功",
|
||||
"redis_result": "Redis结果",
|
||||
"redis_success": "Redis成功",
|
||||
"redis_key": "Redis 键",
|
||||
"redis_value": "Redis 值",
|
||||
"plugin_input": "插件输入",
|
||||
"tool_result": "工具结果",
|
||||
"tool_success": "工具成功",
|
||||
"mcp_arguments": "工具参数",
|
||||
"memory_result": "记忆结果",
|
||||
"memory_success": "记忆成功",
|
||||
"memory_value": "存储值",
|
||||
"answer": "答案",
|
||||
"dify_success": "Dify成功",
|
||||
"dify_query": "用户输入/查询",
|
||||
"dify_conversation_id": "会话 ID",
|
||||
"search_results": "搜索结果",
|
||||
"knowledge_base_query": "查询内容",
|
||||
"n8n_result": "N8n结果",
|
||||
"n8n_success": "N8n成功",
|
||||
"n8n_payload": "工作流输入数据",
|
||||
"flow_result": "流程结果",
|
||||
"flow_success": "流程成功",
|
||||
"langflow_input": "输入内容",
|
||||
"bot_answer": "机器人回复",
|
||||
"bot_success": "机器人成功",
|
||||
"coze_query": "用户输入/查询",
|
||||
"coze_conversation_id": "会话 ID",
|
||||
"bot_conversation_id": "会话 ID",
|
||||
"webhook_body": "Webhook 请求体",
|
||||
"webhook_headers": "Webhook 请求头",
|
||||
"webhook_query": "Webhook 查询参数",
|
||||
"webhook_method": "Webhook 请求方法",
|
||||
"save": "保存",
|
||||
"undo": "撤销",
|
||||
"redo": "重做",
|
||||
"edgeCondition": "条件表达式",
|
||||
"edgeConditionPlaceholder": "如: ${output.success} == true",
|
||||
"noNodeSelected": "未选中节点",
|
||||
"selectNodeToEdit": "点击节点查看和编辑属性",
|
||||
"dragNodeHint": "拖拽节点到画布",
|
||||
"trigger": "触发器",
|
||||
"triggerDescription": "工作流的起始节点",
|
||||
"messageTrigger": "消息触发",
|
||||
"messageTriggerDescription": "当收到消息时触发",
|
||||
"scheduleTrigger": "定时触发",
|
||||
"scheduleTriggerDescription": "按计划定时触发",
|
||||
"cronTrigger": "定时触发",
|
||||
"cronTriggerDescription": "按定时计划触发工作流",
|
||||
"webhookTrigger": "Webhook 触发",
|
||||
"webhookTriggerDescription": "通过 HTTP 请求触发",
|
||||
"eventTrigger": "事件触发",
|
||||
"eventTriggerDescription": "当系统事件发生时触发",
|
||||
"process": "AI/处理",
|
||||
"processDescription": "数据处理节点",
|
||||
"aiProcess": "AI 处理",
|
||||
"aiProcessDescription": "使用 AI 模型处理消息",
|
||||
"llmCall": "LLM 调用",
|
||||
"llmCallDescription": "调用大语言模型进行对话或生成",
|
||||
"codeProcess": "代码处理",
|
||||
"codeProcessDescription": "执行自定义代码",
|
||||
"codeExecutor": "代码执行",
|
||||
"codeExecutorDescription": "执行 Python/JavaScript 代码",
|
||||
"templateProcess": "模板处理",
|
||||
"templateProcessDescription": "使用模板格式化输出",
|
||||
"httpRequest": "HTTP 请求",
|
||||
"httpRequestDescription": "发送 HTTP 请求",
|
||||
"dataTransform": "数据转换",
|
||||
"dataTransformDescription": "转换数据格式",
|
||||
"questionClassifier": "问题分类器",
|
||||
"questionClassifierDescription": "使用 LLM 将用户问题分类到预定义类别",
|
||||
"parameterExtractor": "参数提取器",
|
||||
"parameterExtractorDescription": "使用 LLM 从文本中提取结构化参数",
|
||||
"knowledgeRetrieval": "知识库检索",
|
||||
"knowledgeRetrievalDescription": "从知识库中检索相关内容",
|
||||
"textTemplate": "文本模板",
|
||||
"textTemplateDescription": "使用模板生成文本",
|
||||
"jsonTransform": "JSON 转换",
|
||||
"jsonTransformDescription": "转换 JSON 数据",
|
||||
"dataAggregator": "数据聚合",
|
||||
"dataAggregatorDescription": "聚合多个数据源",
|
||||
"textSplitter": "文本分割",
|
||||
"textSplitterDescription": "将文本分割成块",
|
||||
"variableAssignment": "变量赋值",
|
||||
"variableAssignmentDescription": "为工作流变量赋值",
|
||||
"control": "控制流",
|
||||
"controlDescription": "流程控制节点",
|
||||
"conditionDescription": "根据条件分流",
|
||||
"switch": "多路分支",
|
||||
"switchDescription": "多条件分支选择",
|
||||
"loop": "循环",
|
||||
"loopDescription": "重复执行",
|
||||
"iterator": "迭代器",
|
||||
"iteratorDescription": "遍历数组元素",
|
||||
"parallel": "并行处理",
|
||||
"parallelDescription": "并行执行多个分支",
|
||||
"wait": "等待",
|
||||
"waitDescription": "等待指定时间",
|
||||
"delay": "延迟",
|
||||
"delayDescription": "等待指定时间",
|
||||
"merge": "合并",
|
||||
"mergeDescription": "合并多个分支",
|
||||
"variableAggregator": "变量聚合器",
|
||||
"variableAggregatorDescription": "聚合多个分支的变量输出",
|
||||
"action": "动作",
|
||||
"actionDescription": "执行动作的节点",
|
||||
"sendMessage": "发送消息",
|
||||
"sendMessageDescription": "发送消息到平台",
|
||||
"replyMessage": "回复消息",
|
||||
"replyMessageDescription": "回复触发工作流的消息",
|
||||
"storeData": "存储数据",
|
||||
"storeDataDescription": "存储数据到数据库",
|
||||
"callPipeline": "调用 Pipeline",
|
||||
"callPipelineDescription": "调用现有的 Pipeline",
|
||||
"setVariable": "设置变量",
|
||||
"setVariableDescription": "设置上下文变量",
|
||||
"openingStatement": "对话开场白",
|
||||
"openingStatementDescription": "提供对话开场白和建议问题",
|
||||
"end": "结束",
|
||||
"endDescription": "标记工作流执行结束",
|
||||
"log": "日志",
|
||||
"logDescription": "记录日志信息",
|
||||
"integration": "集成",
|
||||
"integrationDescription": "第三方平台集成节点",
|
||||
"difyWorkflow": "Dify 工作流",
|
||||
"difyWorkflowDescription": "调用 Dify 平台工作流",
|
||||
"difyKnowledgeQuery": "Dify 知识库",
|
||||
"difyKnowledgeQueryDescription": "查询 Dify 知识库",
|
||||
"n8nWorkflow": "n8n 工作流",
|
||||
"n8nWorkflowDescription": "调用 n8n 工作流",
|
||||
"langflowFlow": "Langflow 流程",
|
||||
"langflowFlowDescription": "调用 Langflow 流程",
|
||||
"cozeBot": "Coze Bot",
|
||||
"cozeBotDescription": "调用扣子 Bot",
|
||||
"databaseQuery": "数据库查询",
|
||||
"databaseQueryDescription": "执行数据库查询",
|
||||
"redisOperation": "Redis 操作",
|
||||
"redisOperationDescription": "执行 Redis 缓存操作",
|
||||
"mcpTool": "MCP 工具",
|
||||
"mcpToolDescription": "调用 MCP 工具",
|
||||
"memoryStore": "记忆存储",
|
||||
"memoryStoreDescription": "从工作流记忆中存储和检索数据",
|
||||
"startTime": "开始时间",
|
||||
"running": "执行中",
|
||||
"failed": "失败",
|
||||
"cancelled": "已取消",
|
||||
"viewDetails": "查看详情",
|
||||
"cancel": "取消执行",
|
||||
"retry": "重试",
|
||||
"nodeResults": "节点执行结果",
|
||||
"current": "当前版本",
|
||||
"rollback": "回滚到此版本",
|
||||
"rollbackConfirm": "确定回滚到此版本?当前更改将丢失。",
|
||||
"rollbackSuccess": "回滚成功",
|
||||
"rollbackError": "回滚失败:",
|
||||
"mode": "调试模式",
|
||||
"panel": "调试面板",
|
||||
"start": "开始调试",
|
||||
"pause": "暂停",
|
||||
"resume": "继续",
|
||||
"step": "单步执行",
|
||||
"stop": "停止",
|
||||
"messageContentPlaceholder": "输入要模拟的消息内容",
|
||||
"senderId": "发送者 ID",
|
||||
"senderIdPlaceholder": "发送者唯一标识",
|
||||
"senderName": "发送者名称",
|
||||
"senderNamePlaceholder": "发送者显示名称",
|
||||
"platformPlaceholder": "例如 qq、wechat、telegram",
|
||||
"conversationId": "会话 ID",
|
||||
"conversationIdPlaceholder": "会话唯一标识",
|
||||
"isGroup": "群聊",
|
||||
"customVariables": "自定义变量",
|
||||
"customVariablesDesc": "添加自定义变量用于调试",
|
||||
"variableKey": "变量名",
|
||||
"variableValue": "变量值",
|
||||
"addVariable": "添加变量",
|
||||
"watchedVariables": "监控变量",
|
||||
"noWatchedVariables": "暂无监控变量",
|
||||
"addWatchVariable": "添加监控",
|
||||
"nodeStates": "节点状态",
|
||||
"nodeOutputs": "节点输出",
|
||||
"noNodeOutputs": "暂无节点输出",
|
||||
"toggleBreakpoint": "切换断点",
|
||||
"clearBreakpoints": "清除所有断点",
|
||||
"breakpointSet": "断点已设置",
|
||||
"breakpointRemoved": "断点已移除",
|
||||
"noLogs": "暂无日志",
|
||||
"clearLogs": "清空日志",
|
||||
"autoScroll": "自动滚动",
|
||||
"logEntries": "条日志",
|
||||
"resetContext": "重置上下文",
|
||||
"starting": "正在启动调试执行...",
|
||||
"started": "调试执行已启动 (ID: {{id}})",
|
||||
"startError": "启动调试失败",
|
||||
"unknownError": "未知错误",
|
||||
"paused": "已暂停",
|
||||
"pauseError": "暂停失败",
|
||||
"resumed": "执行已恢复",
|
||||
"resumeError": "恢复失败",
|
||||
"steppedTo": "已执行到节点: {{node}}",
|
||||
"stepError": "单步执行失败",
|
||||
"stopped": "调试已停止",
|
||||
"stopError": "停止失败",
|
||||
"debugMode": "调试模式",
|
||||
"debugPanel": "调试面板",
|
||||
"startDebug": "开始调试",
|
||||
"pauseDebug": "暂停",
|
||||
"resumeDebug": "继续",
|
||||
"stepDebug": "单步执行",
|
||||
"stopDebug": "停止",
|
||||
"debugContext": "调试上下文",
|
||||
"simulatedMessage": "模拟消息",
|
||||
"simulatedMessagePlaceholder": "输入要模拟的消息内容",
|
||||
"variableName": "变量名",
|
||||
"breakpoints": "断点",
|
||||
"debugLogs": "调试日志",
|
||||
"idle": "空闲",
|
||||
"pending": "等待中",
|
||||
"skipped": "已跳过",
|
||||
"selectWorkflow": "选择工作流",
|
||||
"sessionType": "会话类型",
|
||||
"privateChat": "私聊",
|
||||
"groupChat": "群聊",
|
||||
"send": "发送",
|
||||
"reset": "重置对话",
|
||||
"inputPlaceholder": "发送 {{type}} 消息...",
|
||||
"noMessages": "暂无消息",
|
||||
"userMessage": "用户",
|
||||
"botMessage": "机器人",
|
||||
"sendFailed": "发送失败",
|
||||
"resetSuccess": "对话已重置",
|
||||
"resetFailed": "重置失败",
|
||||
"loadMessagesFailed": "加载消息失败",
|
||||
"loadWorkflowsFailed": "加载工作流失败",
|
||||
"atTips": "提及机器人",
|
||||
"streaming": "流式传输",
|
||||
"streamOutput": "流式",
|
||||
"connected": "WebSocket已连接",
|
||||
"disconnected": "WebSocket未连接",
|
||||
"connectionError": "WebSocket连接错误",
|
||||
"connectionFailed": "WebSocket连接失败",
|
||||
"notConnected": "WebSocket未连接,请稍后重试",
|
||||
"imageUploadFailed": "图片上传失败",
|
||||
"reply": "回复",
|
||||
"replyTo": "回复给",
|
||||
"showMarkdown": "渲染",
|
||||
"showRaw": "原文",
|
||||
"allMembers": "全体成员",
|
||||
"file": "文件",
|
||||
"voice": "语音",
|
||||
"uploadImage": "上传图片",
|
||||
"uploading": "上传中...",
|
||||
"filterByDate": "按日期筛选",
|
||||
"allTime": "全部时间",
|
||||
"today": "今天",
|
||||
"lastWeek": "最近一周",
|
||||
"lastMonth": "最近一个月",
|
||||
"showingExecutions": "显示 {{shown}} / {{total}} 条记录",
|
||||
"rerun": "重新运行",
|
||||
"rerunExecution": "重新执行",
|
||||
"details": "详情",
|
||||
"completedAt": "完成时间",
|
||||
"noNodeExecutions": "暂无节点执行记录",
|
||||
"conditions": "触发条件",
|
||||
"keyword_filter": "关键词过滤",
|
||||
"regex_filter": "正则过滤",
|
||||
"min_length": "最小长度",
|
||||
"max_length": "最大长度",
|
||||
"require_mention": "需要@机器人",
|
||||
"respond_rules": "群响应规则",
|
||||
"access_control": "访问控制",
|
||||
"cron": "Cron表达式",
|
||||
"timezone": "时区",
|
||||
"path": "Webhook路径",
|
||||
"allowed_methods": "允许的HTTP方法",
|
||||
"content_type": "Content-Type",
|
||||
"auth_type": "认证方式",
|
||||
"auth_key": "认证密钥",
|
||||
"validation": "请求验证",
|
||||
"timeout": "超时时间",
|
||||
"event_types": "事件类型",
|
||||
"filter": "事件过滤",
|
||||
"debounce_ms": "防抖时间",
|
||||
"model": "模型",
|
||||
"prompt_template": "提示词模板",
|
||||
"system_prompt": "系统提示词",
|
||||
"temperature": "温度",
|
||||
"top_p": "Top P",
|
||||
"frequency_penalty": "频率惩罚",
|
||||
"presence_penalty": "存在惩罚",
|
||||
"max_tokens": "最大Token数",
|
||||
"stop_sequences": "停止序列",
|
||||
"seed": "随机种子",
|
||||
"stream": "流式输出",
|
||||
"use_conversation_history": "使用对话历史",
|
||||
"language": "编程语言",
|
||||
"code": "代码",
|
||||
"url": "请求URL",
|
||||
"auth_config": "认证配置",
|
||||
"transform_type": "转换类型",
|
||||
"template": "模板",
|
||||
"expression": "表达式",
|
||||
"output_type": "输出类型",
|
||||
"categories": "分类类别",
|
||||
"instruction": "指令",
|
||||
"knowledge_bases": "知识库",
|
||||
"top_k": "返回数量",
|
||||
"score_threshold": "相似度阈值",
|
||||
"search_method": "搜索方法",
|
||||
"enable_citations": "启用引用",
|
||||
"condition_type": "条件类型",
|
||||
"condition_expression": "条件表达式",
|
||||
"left_value": "左值",
|
||||
"operator": "比较运算符",
|
||||
"right_value": "右值",
|
||||
"cases": "分支条件",
|
||||
"max_iterations": "最大迭代次数",
|
||||
"break_condition": "中断条件",
|
||||
"max_concurrency": "最大并发数",
|
||||
"branches": "分支配置",
|
||||
"wait_all": "等待所有",
|
||||
"fail_fast": "快速失败",
|
||||
"duration_type": "时间单位",
|
||||
"merge_strategy": "合并策略",
|
||||
"variable_mappings": "变量映射",
|
||||
"aggregation_mode": "聚合模式",
|
||||
"target_type": "目标类型",
|
||||
"target_id": "目标ID",
|
||||
"message_type": "消息类型",
|
||||
"reply_mode": "回复模式",
|
||||
"message_template": "消息模板",
|
||||
"long_text_processing": "长文本处理",
|
||||
"force_delay": "强制延迟",
|
||||
"pipeline_uuid": "流水线",
|
||||
"inherit_context": "继承上下文",
|
||||
"storage_type": "存储类型",
|
||||
"ttl": "过期时间",
|
||||
"key_prefix": "键前缀",
|
||||
"variable_name": "变量名称",
|
||||
"variable_scope": "变量作用域",
|
||||
"set_variable_operation": "操作类型",
|
||||
"show_suggestions": "显示建议",
|
||||
"output_format": "输出格式",
|
||||
"success_message": "成功消息",
|
||||
"connection_type": "数据库类型",
|
||||
"connection_string": "连接字符串",
|
||||
"query_type": "查询类型",
|
||||
"connection_url": "连接URL",
|
||||
"operation": "操作类型",
|
||||
"dataset_id": "知识库ID",
|
||||
"escape_html": "转义HTML",
|
||||
"trim_whitespace": "去除空白",
|
||||
"json_transform_type": "JSON转换类型",
|
||||
"json_expression": "JSON表达式",
|
||||
"mapping": "字段映射",
|
||||
"code_language": "编程语言",
|
||||
"code_content": "代码内容",
|
||||
"aggregation_type": "聚合类型",
|
||||
"separator": "分隔符",
|
||||
"field_path": "字段路径",
|
||||
"split_type": "分割类型",
|
||||
"chunk_size": "块大小",
|
||||
"chunk_overlap": "块重叠",
|
||||
"regex_pattern": "正则表达式",
|
||||
"remove_empty": "移除空块",
|
||||
"assign_variable_name": "变量名",
|
||||
"value_type": "值类型",
|
||||
"static_value": "静态值",
|
||||
"n8n_webhook_url": "Webhook URL",
|
||||
"n8n_auth_type": "认证方式",
|
||||
"langflow_flow_id": "流程ID",
|
||||
"coze_bot_id": "机器人ID",
|
||||
"coze_api_base": "API基础URL"
|
||||
}
|
||||
831
web/workflows_section.txt
Normal file
831
web/workflows_section.txt
Normal file
@@ -0,0 +1,831 @@
|
||||
workflows: {
|
||||
title: '工作流',
|
||||
description: '创建和管理可视化工作流,实现复杂的消息处理逻辑',
|
||||
createWorkflow: '创建工作流',
|
||||
selectFromSidebar: '从侧边栏选择一个工作流',
|
||||
editWorkflow: '编辑工作流',
|
||||
newWorkflow: '新工作流',
|
||||
getWorkflowListError: '获取工作流列表失败:',
|
||||
workflowName: '工作流名称',
|
||||
workflowDescription: '工作流描述',
|
||||
workflowNameRequired: '工作流名称不能为空',
|
||||
defaultDescription: '一个工作流',
|
||||
getWorkflowError: '获取工作流失败:',
|
||||
loadError: '加载工作流失败',
|
||||
saveSuccess: '保存成功',
|
||||
saveError: '保存失败:',
|
||||
createSuccess: '工作流创建成功',
|
||||
createError: '创建失败:',
|
||||
deleteSuccess: '删除成功',
|
||||
deleteError: '删除失败:',
|
||||
deleteConfirmation: '你确定要删除这个工作流吗?',
|
||||
copySuccess: '复制成功',
|
||||
copyError: '复制失败:',
|
||||
export: '导出',
|
||||
import: '导入',
|
||||
exportSuccess: '工作流已导出',
|
||||
importSuccess: '工作流已导入',
|
||||
importError: '导入失败:文件格式无效',
|
||||
publish: '发布',
|
||||
publishSuccess: '发布成功',
|
||||
publishError: '发布失败',
|
||||
configuration: '配置',
|
||||
executions: '执行记录',
|
||||
editor: '编辑器',
|
||||
debugChat: '对话调试',
|
||||
basicInfo: '基础信息',
|
||||
basicInfoDesc: '设置工作流名称、图标和描述',
|
||||
basicInfoDescription: '设置工作流名称和描述',
|
||||
dangerZone: '危险区域',
|
||||
dangerZoneDesc: '不可逆的操作',
|
||||
dangerZoneDescription: '不可逆的操作',
|
||||
deleteWorkflowAction: '删除此工作流',
|
||||
deleteWorkflowHint: '删除后,所有关联配置将被永久移除,且无法恢复。',
|
||||
deleteWorkflow: '删除工作流',
|
||||
deleteConfirm: '确认删除',
|
||||
deleteConfirmDesc: '您确定要删除工作流 "{{name}}" 吗?此操作无法撤销。',
|
||||
// Form component
|
||||
name: '名称',
|
||||
namePlaceholder: '输入工作流名称',
|
||||
descriptionPlaceholder: '输入工作流描述(可选)',
|
||||
enabled: '启用',
|
||||
enabledDesc: '启用后,工作流将可以被触发执行',
|
||||
loading: '加载中...',
|
||||
info: '工作流信息',
|
||||
uuid: 'UUID',
|
||||
version: '版本',
|
||||
createdAt: '创建时间',
|
||||
updatedAt: '更新时间',
|
||||
// Executions tab
|
||||
totalExecutions: '共 {{count}} 条执行记录',
|
||||
statistics: '统计分析',
|
||||
successfulCount: '成功 {{count}} 次',
|
||||
successRate: '成功率',
|
||||
averageDuration: '平均耗时',
|
||||
perExecution: '每次执行',
|
||||
failedExecutions: '失败次数',
|
||||
lastExecution: '最后执行',
|
||||
filterByStatus: '按状态筛选',
|
||||
allStatuses: '全部状态',
|
||||
manualTrigger: '手动触发',
|
||||
executionId: '执行 ID',
|
||||
status: '状态',
|
||||
triggerType: '触发类型',
|
||||
startedAt: '开始时间',
|
||||
duration: '耗时',
|
||||
noExecutions: '暂无执行记录',
|
||||
executionDetails: '执行详情',
|
||||
error: '错误信息',
|
||||
nodeExecutions: '节点执行记录',
|
||||
result: '执行结果',
|
||||
'status.pending': '等待中',
|
||||
'status.waiting': '等待中',
|
||||
'status.running': '执行中',
|
||||
'status.completed': '已完成',
|
||||
'status.failed': '失败',
|
||||
'status.cancelled': '已取消',
|
||||
// Editor component translations
|
||||
nodePalette: '节点面板',
|
||||
properties: '属性',
|
||||
zoomIn: '放大',
|
||||
zoomOut: '缩小',
|
||||
fitView: '适应视图',
|
||||
unsavedChanges: '有未保存的更改',
|
||||
paste: '粘贴',
|
||||
deleted: '已删除',
|
||||
nothingToCopy: '没有选中的节点可复制',
|
||||
nothingToPaste: '剪贴板为空',
|
||||
copied: '已复制 {{count}} 个节点',
|
||||
pasted: '已粘贴 {{count}} 个节点',
|
||||
nodesSelected: '已选中 {{count}} 个节点',
|
||||
edgesSelected: '已选中 {{count}} 条连线',
|
||||
// Node palette
|
||||
searchNodes: '搜索节点...',
|
||||
loadingNodeTypes: '正在加载节点类型...',
|
||||
noNodesFound: '未找到匹配的节点',
|
||||
clearSearch: '清除搜索',
|
||||
dragToAdd: '拖拽节点到画布添加',
|
||||
// Property panel
|
||||
selectNodeOrEdge: '选择一个节点或连线',
|
||||
selectNodeOrEdgeHint: '点击画布中的节点或连线来查看和编辑其属性',
|
||||
edgeProperties: '连线属性',
|
||||
nodeProperties: '节点属性',
|
||||
condition: '条件',
|
||||
hasCondition: '已设置',
|
||||
conditionPlaceholder: '输入条件表达式,如: output.success == true',
|
||||
conditionHelp:
|
||||
'条件为空时,该连线将始终被执行。支持使用 {{变量名}} 引用上下文变量。',
|
||||
deleteEdge: '删除连线',
|
||||
deleteEdgeConfirm: '确认删除连线',
|
||||
deleteEdgeConfirmDesc: '删除后,该连线将被永久移除。',
|
||||
nodeLabel: '节点名称',
|
||||
nodeLabelPlaceholder: '输入节点显示名称',
|
||||
nodeId: '节点 ID',
|
||||
inputOutputVariables: '输入/输出变量',
|
||||
inputs: '输入',
|
||||
outputs: '输出',
|
||||
availableVariables: '可用变量',
|
||||
globalVariables: '全局变量',
|
||||
messageContent: '消息内容',
|
||||
messageSender: '发送者',
|
||||
platform: '平台',
|
||||
sessionId: '会话 ID',
|
||||
timestamp: '时间戳',
|
||||
nodeConfig: '节点配置',
|
||||
noConfigOptions: '该节点类型暂无配置选项',
|
||||
deleteNode: '删除节点',
|
||||
deleteNodeConfirm: '确认删除节点',
|
||||
deleteNodeConfirmDesc: '删除后,该节点及其所有连线将被永久移除。',
|
||||
// Node inputs/outputs i18n (for port labels)
|
||||
nodeInputs: {
|
||||
// Common inputs
|
||||
input: '输入',
|
||||
message: '消息内容',
|
||||
text: '文本',
|
||||
query: '查询',
|
||||
data: '数据',
|
||||
condition: '条件',
|
||||
value: '值',
|
||||
// Trigger inputs
|
||||
content: '内容',
|
||||
context: '上下文',
|
||||
body: '请求体',
|
||||
variables: '变量',
|
||||
items: '项目列表',
|
||||
arguments: '参数',
|
||||
// AI/Process inputs
|
||||
question: '问题',
|
||||
parameters: '参数定义',
|
||||
key: '键',
|
||||
payload: '载荷',
|
||||
input_value: '输入值',
|
||||
conversation_id: '会话 ID',
|
||||
// Control inputs
|
||||
case_1: '分支1输入',
|
||||
case_2: '分支2输入',
|
||||
branch_1: '分支1',
|
||||
branch_2: '分支2',
|
||||
// Action inputs
|
||||
notification_id: '通知ID',
|
||||
// Integration inputs
|
||||
key_template: '键模板',
|
||||
hash_field: '哈希字段',
|
||||
server_name: '服务器名称',
|
||||
tool_name: '工具名称',
|
||||
arguments_template: '参数模板',
|
||||
scope: '作用域',
|
||||
},
|
||||
nodeOutputs: {
|
||||
// Common outputs
|
||||
output: '输出',
|
||||
result: '结果',
|
||||
response: '响应',
|
||||
message: '消息',
|
||||
data: '数据',
|
||||
error: '错误',
|
||||
// Trigger outputs
|
||||
sender: '发送者',
|
||||
sender_id: '发送者 ID',
|
||||
sender_name: '发送者名称',
|
||||
conversation_id: '会话 ID',
|
||||
context: '上下文',
|
||||
cron_timestamp: 'Cron 时间戳',
|
||||
cron_schedule: 'Cron 调度表达式',
|
||||
cron_context: 'Cron 上下文',
|
||||
trigger_time: '触发时间',
|
||||
schedule: '触发计划',
|
||||
headers: '请求头',
|
||||
query_params: '查询参数',
|
||||
query: '查询参数',
|
||||
method: '请求方法',
|
||||
body: '请求体',
|
||||
is_group: '是否群聊',
|
||||
platform: '平台',
|
||||
event: '事件',
|
||||
event_type: '事件类型',
|
||||
event_data: '事件数据',
|
||||
event_timestamp: '事件时间戳',
|
||||
// AI/Process outputs
|
||||
usage: 'Token使用统计',
|
||||
parsed: '解析结果',
|
||||
category: '分类结果',
|
||||
confidence: '置信度',
|
||||
all_scores: '所有分数',
|
||||
missing: '缺失参数',
|
||||
success: '是否成功',
|
||||
scores: '分数',
|
||||
chunks: '文本块',
|
||||
count: '数量',
|
||||
logs: '日志',
|
||||
embedding: '向量',
|
||||
dimensions: '维度',
|
||||
intent: '意图',
|
||||
entities: '实体',
|
||||
prompt: '提示/问题',
|
||||
context_info: '上下文信息',
|
||||
console: '控制台输出',
|
||||
code_input: '代码输入',
|
||||
code_output: '代码输出',
|
||||
http_body: 'HTTP 请求体',
|
||||
http_headers: 'HTTP 请求头',
|
||||
http_response: 'HTTP 响应',
|
||||
response_headers: '响应头',
|
||||
transform_input: '转换输入',
|
||||
transform_result: '转换结果',
|
||||
question: '用户问题',
|
||||
parameters: '提取的参数',
|
||||
extraction_success: '提取成功',
|
||||
extract_text: '输入文本',
|
||||
documents: '检索的文档',
|
||||
citations: '引用信息',
|
||||
knowledge_context: '合并上下文',
|
||||
knowledge_query: '检索查询',
|
||||
text: '文本',
|
||||
// Control outputs
|
||||
true: '条件为真输出',
|
||||
false: '条件为假输出',
|
||||
matched_case: '匹配分支',
|
||||
case_1: '分支 1',
|
||||
case_2: '分支 2',
|
||||
default: '默认分支',
|
||||
item: '当前项',
|
||||
index: '当前索引',
|
||||
completed: '是否完成',
|
||||
is_first: '是否第一个',
|
||||
is_last: '是否最后一个',
|
||||
results: '结果',
|
||||
branch_1: '分支1输出',
|
||||
branch_2: '分支2输出',
|
||||
condition_input: '条件输入',
|
||||
switch_input: '开关输入',
|
||||
loop_items: '迭代项目',
|
||||
iterator_array: '输入数组',
|
||||
iterator_item: '当前元素',
|
||||
iterator_index: '当前索引',
|
||||
errors: '错误列表',
|
||||
parallel_input: '并行输入',
|
||||
parallel_results: '所有分支结果',
|
||||
wait_input: '透传输入',
|
||||
wait_output: '透传输出',
|
||||
merged: '合并结果',
|
||||
merge_array: '数组结果',
|
||||
merge_input_1: '输入 1',
|
||||
merge_input_2: '输入 2',
|
||||
merge_input_3: '输入 3',
|
||||
merge_input_4: '输入 4',
|
||||
aggregated: '聚合变量',
|
||||
aggregator_variables: '变量输入',
|
||||
// Action outputs
|
||||
message_id: '消息ID',
|
||||
status_code: '状态码',
|
||||
status: '状态',
|
||||
execution_id: '执行ID',
|
||||
notification_id: '通知ID',
|
||||
target: '目标 ID',
|
||||
reply_message: '回复内容',
|
||||
pipeline_response: 'Pipeline 响应',
|
||||
pipeline_result: '完整结果',
|
||||
pipeline_query: '查询内容',
|
||||
context_data: '上下文数据',
|
||||
store_status: '存储状态',
|
||||
store_key: '存储键',
|
||||
store_value: '存储值',
|
||||
variable_value: '变量值',
|
||||
variable_result: '设置变量结果',
|
||||
statement: '开场白',
|
||||
suggested_questions: '建议问题',
|
||||
suggestions: '建议问题',
|
||||
workflow_output: '工作流输出',
|
||||
final_result: '最终结果',
|
||||
// Integration outputs
|
||||
query_results: '查询结果',
|
||||
row_count: '行数',
|
||||
query_success: '查询成功',
|
||||
redis_result: 'Redis结果',
|
||||
redis_success: 'Redis成功',
|
||||
redis_key: 'Redis 键',
|
||||
redis_value: 'Redis 值',
|
||||
plugin_input: '插件输入',
|
||||
tool_result: '工具结果',
|
||||
tool_success: '工具成功',
|
||||
mcp_arguments: '工具参数',
|
||||
memory_result: '记忆结果',
|
||||
memory_success: '记忆成功',
|
||||
memory_value: '存储值',
|
||||
answer: '答案',
|
||||
dify_success: 'Dify成功',
|
||||
dify_query: '用户输入/查询',
|
||||
dify_conversation_id: '会话 ID',
|
||||
search_results: '搜索结果',
|
||||
knowledge_base_query: '查询内容',
|
||||
n8n_result: 'N8n结果',
|
||||
n8n_success: 'N8n成功',
|
||||
n8n_payload: '工作流输入数据',
|
||||
flow_result: '流程结果',
|
||||
flow_success: '流程成功',
|
||||
langflow_input: '输入内容',
|
||||
bot_answer: '机器人回复',
|
||||
bot_success: '机器人成功',
|
||||
coze_query: '用户输入/查询',
|
||||
coze_conversation_id: '会话 ID',
|
||||
bot_conversation_id: '会话 ID',
|
||||
webhook_body: 'Webhook 请求体',
|
||||
webhook_headers: 'Webhook 请求头',
|
||||
webhook_query: 'Webhook 查询参数',
|
||||
webhook_method: 'Webhook 请求方法',
|
||||
timestamp: '时间戳',
|
||||
},
|
||||
// Data type labels
|
||||
'type.string': '字符串',
|
||||
'type.object': '对象',
|
||||
'type.array': '数组',
|
||||
'type.boolean': '布尔值',
|
||||
'type.number': '数字',
|
||||
'type.any': '任意类型',
|
||||
'type.datetime': '日期时间',
|
||||
// Legacy editor keys for compatibility
|
||||
legacyEditor: {
|
||||
title: '可视化编辑器',
|
||||
save: '保存',
|
||||
undo: '撤销',
|
||||
redo: '重做',
|
||||
zoomIn: '放大',
|
||||
zoomOut: '缩小',
|
||||
fitView: '适应视图',
|
||||
unsavedChanges: '有未保存的更改',
|
||||
nodePalette: '节点面板',
|
||||
properties: '属性',
|
||||
nodeLabel: '节点名称',
|
||||
nodeConfig: '节点配置',
|
||||
deleteNode: '删除节点',
|
||||
deleteNodeConfirm: '确定删除此节点?',
|
||||
deleteEdge: '删除连线',
|
||||
deleteEdgeConfirm: '确定删除此连线?',
|
||||
edgeCondition: '条件表达式',
|
||||
edgeConditionPlaceholder: '如: ${output.success} == true',
|
||||
noNodeSelected: '未选中节点',
|
||||
selectNodeToEdit: '点击节点查看和编辑属性',
|
||||
dragNodeHint: '拖拽节点到画布',
|
||||
},
|
||||
nodes: {
|
||||
trigger: '触发器',
|
||||
triggerDescription: '工作流的起始节点',
|
||||
messageTrigger: '消息触发',
|
||||
messageTriggerDescription: '当收到消息时触发',
|
||||
scheduleTrigger: '定时触发',
|
||||
scheduleTriggerDescription: '按计划定时触发',
|
||||
cronTrigger: '定时触发',
|
||||
cronTriggerDescription: '按定时计划触发工作流',
|
||||
webhookTrigger: 'Webhook 触发',
|
||||
webhookTriggerDescription: '通过 HTTP 请求触发',
|
||||
eventTrigger: '事件触发',
|
||||
eventTriggerDescription: '当系统事件发生时触发',
|
||||
process: 'AI/处理',
|
||||
processDescription: '数据处理节点',
|
||||
aiProcess: 'AI 处理',
|
||||
aiProcessDescription: '使用 AI 模型处理消息',
|
||||
llmCall: 'LLM 调用',
|
||||
llmCallDescription: '调用大语言模型进行对话或生成',
|
||||
codeProcess: '代码处理',
|
||||
codeProcessDescription: '执行自定义代码',
|
||||
codeExecutor: '代码执行',
|
||||
codeExecutorDescription: '执行 Python/JavaScript 代码',
|
||||
templateProcess: '模板处理',
|
||||
templateProcessDescription: '使用模板格式化输出',
|
||||
httpRequest: 'HTTP 请求',
|
||||
httpRequestDescription: '发送 HTTP 请求',
|
||||
dataTransform: '数据转换',
|
||||
dataTransformDescription: '转换数据格式',
|
||||
questionClassifier: '问题分类器',
|
||||
questionClassifierDescription: '使用 LLM 将用户问题分类到预定义类别',
|
||||
parameterExtractor: '参数提取器',
|
||||
parameterExtractorDescription: '使用 LLM 从文本中提取结构化参数',
|
||||
knowledgeRetrieval: '知识库检索',
|
||||
knowledgeRetrievalDescription: '从知识库中检索相关内容',
|
||||
textTemplate: '文本模板',
|
||||
textTemplateDescription: '使用模板生成文本',
|
||||
jsonTransform: 'JSON 转换',
|
||||
jsonTransformDescription: '转换 JSON 数据',
|
||||
dataAggregator: '数据聚合',
|
||||
dataAggregatorDescription: '聚合多个数据源',
|
||||
textSplitter: '文本分割',
|
||||
textSplitterDescription: '将文本分割成块',
|
||||
variableAssignment: '变量赋值',
|
||||
variableAssignmentDescription: '为工作流变量赋值',
|
||||
control: '控制流',
|
||||
controlDescription: '流程控制节点',
|
||||
condition: '条件分支',
|
||||
conditionDescription: '根据条件分流',
|
||||
switch: '多路分支',
|
||||
switchDescription: '多条件分支选择',
|
||||
loop: '循环',
|
||||
loopDescription: '重复执行',
|
||||
iterator: '迭代器',
|
||||
iteratorDescription: '遍历数组元素',
|
||||
parallel: '并行执行',
|
||||
parallelDescription: '并行执行多个分支',
|
||||
wait: '等待',
|
||||
waitDescription: '等待指定时间',
|
||||
delay: '延迟',
|
||||
delayDescription: '等待指定时间',
|
||||
merge: '合并',
|
||||
mergeDescription: '合并多个分支',
|
||||
variableAggregator: '变量聚合器',
|
||||
variableAggregatorDescription: '聚合多个分支的变量输出',
|
||||
action: '动作',
|
||||
actionDescription: '执行动作的节点',
|
||||
sendMessage: '发送消息',
|
||||
sendMessageDescription: '发送消息到平台',
|
||||
replyMessage: '回复消息',
|
||||
replyMessageDescription: '回复触发工作流的消息',
|
||||
storeData: '存储数据',
|
||||
storeDataDescription: '存储数据到数据库',
|
||||
callPipeline: '调用 Pipeline',
|
||||
callPipelineDescription: '调用现有的 Pipeline',
|
||||
setVariable: '设置变量',
|
||||
setVariableDescription: '设置上下文变量',
|
||||
openingStatement: '对话开场白',
|
||||
openingStatementDescription: '提供对话开场白和建议问题',
|
||||
end: '结束',
|
||||
endDescription: '标记工作流执行结束',
|
||||
log: '日志',
|
||||
logDescription: '记录日志信息',
|
||||
integration: '集成',
|
||||
integrationDescription: '第三方平台集成节点',
|
||||
difyWorkflow: 'Dify 工作流',
|
||||
difyWorkflowDescription: '调用 Dify 平台工作流',
|
||||
difyKnowledgeQuery: 'Dify 知识库',
|
||||
difyKnowledgeQueryDescription: '查询 Dify 知识库',
|
||||
n8nWorkflow: 'n8n 工作流',
|
||||
n8nWorkflowDescription: '调用 n8n 工作流',
|
||||
langflowFlow: 'Langflow 流程',
|
||||
langflowFlowDescription: '调用 Langflow 流程',
|
||||
cozeBot: 'Coze Bot',
|
||||
cozeBotDescription: '调用扣子 Bot',
|
||||
// Data & Tools integration nodes
|
||||
databaseQuery: '数据库查询',
|
||||
databaseQueryDescription: '执行数据库查询',
|
||||
redisOperation: 'Redis 操作',
|
||||
redisOperationDescription: '执行 Redis 缓存操作',
|
||||
mcpTool: 'MCP 工具',
|
||||
mcpToolDescription: '调用 MCP 工具',
|
||||
memoryStore: '记忆存储',
|
||||
memoryStoreDescription: '从工作流记忆中存储和检索数据',
|
||||
},
|
||||
executionHistory: {
|
||||
title: '执行记录',
|
||||
noExecutions: '暂无执行记录',
|
||||
status: '状态',
|
||||
startTime: '开始时间',
|
||||
duration: '耗时',
|
||||
running: '运行中',
|
||||
completed: '已完成',
|
||||
failed: '失败',
|
||||
cancelled: '已取消',
|
||||
viewDetails: '查看详情',
|
||||
cancel: '取消执行',
|
||||
retry: '重试',
|
||||
nodeResults: '节点执行结果',
|
||||
},
|
||||
versions: {
|
||||
title: '版本历史',
|
||||
current: '当前版本',
|
||||
rollback: '回滚到此版本',
|
||||
rollbackConfirm: '确定回滚到此版本?当前更改将丢失。',
|
||||
rollbackSuccess: '回滚成功',
|
||||
rollbackError: '回滚失败:',
|
||||
},
|
||||
// Debug and monitoring
|
||||
debug: {
|
||||
title: '调试',
|
||||
mode: '调试模式',
|
||||
panel: '调试面板',
|
||||
start: '开始调试',
|
||||
pause: '暂停',
|
||||
resume: '继续',
|
||||
step: '单步执行',
|
||||
stop: '停止',
|
||||
context: '调试上下文',
|
||||
messageContent: '模拟消息',
|
||||
messageContentPlaceholder: '输入要模拟的消息内容',
|
||||
senderId: '发送者 ID',
|
||||
senderIdPlaceholder: '发送者唯一标识',
|
||||
senderName: '发送者名称',
|
||||
senderNamePlaceholder: '发送者显示名称',
|
||||
platform: '平台',
|
||||
platformPlaceholder: '例如 qq、wechat、telegram',
|
||||
conversationId: '会话 ID',
|
||||
conversationIdPlaceholder: '会话唯一标识',
|
||||
isGroup: '群聊',
|
||||
customVariables: '自定义变量',
|
||||
customVariablesDesc: '添加自定义变量用于调试',
|
||||
variableKey: '变量名',
|
||||
variableValue: '变量值',
|
||||
addVariable: '添加变量',
|
||||
variables: '监控变量',
|
||||
watchedVariables: '监控变量',
|
||||
noWatchedVariables: '暂无监控变量',
|
||||
addWatchVariable: '添加监控',
|
||||
nodeStates: '节点状态',
|
||||
nodeOutputs: '节点输出',
|
||||
noNodeOutputs: '暂无节点输出',
|
||||
toggleBreakpoint: '切换断点',
|
||||
clearBreakpoints: '清除所有断点',
|
||||
breakpointSet: '断点已设置',
|
||||
breakpointRemoved: '断点已移除',
|
||||
logs: '调试日志',
|
||||
noLogs: '暂无日志',
|
||||
clearLogs: '清空日志',
|
||||
autoScroll: '自动滚动',
|
||||
logEntries: '条日志',
|
||||
resetContext: '重置上下文',
|
||||
// Debug execution messages
|
||||
starting: '正在启动调试执行...',
|
||||
started: '调试执行已启动 (ID: {{id}})',
|
||||
startError: '启动调试失败',
|
||||
completed: '调试执行已完成',
|
||||
unknownError: '未知错误',
|
||||
paused: '执行已暂停',
|
||||
pauseError: '暂停失败',
|
||||
resumed: '执行已恢复',
|
||||
resumeError: '恢复失败',
|
||||
steppedTo: '已执行到节点: {{node}}',
|
||||
stepError: '单步执行失败',
|
||||
stopped: '调试已停止',
|
||||
stopError: '停止失败',
|
||||
},
|
||||
debugMode: '调试模式',
|
||||
debugPanel: '调试面板',
|
||||
startDebug: '开始调试',
|
||||
pauseDebug: '暂停',
|
||||
resumeDebug: '继续',
|
||||
stepDebug: '单步执行',
|
||||
stopDebug: '停止',
|
||||
debugContext: '调试上下文',
|
||||
simulatedMessage: '模拟消息',
|
||||
simulatedMessagePlaceholder: '输入要模拟的消息内容',
|
||||
senderId: '发送者 ID',
|
||||
senderIdPlaceholder: '发送者唯一标识',
|
||||
senderName: '发送者名称',
|
||||
senderNamePlaceholder: '发送者显示名称',
|
||||
conversationId: '会话 ID',
|
||||
conversationIdPlaceholder: '会话唯一标识',
|
||||
isGroup: '群聊',
|
||||
customVariables: '自定义变量',
|
||||
addVariable: '添加变量',
|
||||
variableName: '变量名',
|
||||
variableValue: '变量值',
|
||||
watchedVariables: '监控变量',
|
||||
addWatchVariable: '添加监控',
|
||||
nodeStates: '节点状态',
|
||||
breakpoints: '断点',
|
||||
toggleBreakpoint: '切换断点',
|
||||
breakpointSet: '断点已设置',
|
||||
breakpointRemoved: '断点已移除',
|
||||
debugLogs: '调试日志',
|
||||
noLogs: '暂无日志',
|
||||
clearLogs: '清空日志',
|
||||
autoScroll: '自动滚动',
|
||||
debugState: {
|
||||
idle: '空闲',
|
||||
running: '运行中',
|
||||
paused: '已暂停',
|
||||
completed: '已完成',
|
||||
error: '错误',
|
||||
},
|
||||
nodeStatus: {
|
||||
pending: '等待中',
|
||||
running: '执行中',
|
||||
completed: '已完成',
|
||||
failed: '失败',
|
||||
skipped: '已跳过',
|
||||
},
|
||||
debugDialog: {
|
||||
title: '工作流对话',
|
||||
selectWorkflow: '选择工作流',
|
||||
sessionType: '会话类型',
|
||||
privateChat: '私聊',
|
||||
groupChat: '群聊',
|
||||
send: '发送',
|
||||
reset: '重置对话',
|
||||
inputPlaceholder: '发送 {{type}} 消息...',
|
||||
noMessages: '暂无消息',
|
||||
userMessage: '用户',
|
||||
botMessage: '机器人',
|
||||
sendFailed: '发送失败',
|
||||
resetSuccess: '对话已重置',
|
||||
resetFailed: '重置失败',
|
||||
loadMessagesFailed: '加载消息失败',
|
||||
loadWorkflowsFailed: '加载工作流失败',
|
||||
atTips: '提及机器人',
|
||||
streaming: '流式传输',
|
||||
streamOutput: '流式',
|
||||
connected: 'WebSocket已连接',
|
||||
disconnected: 'WebSocket未连接',
|
||||
connectionError: 'WebSocket连接错误',
|
||||
connectionFailed: 'WebSocket连接失败',
|
||||
notConnected: 'WebSocket未连接,请稍后重试',
|
||||
imageUploadFailed: '图片上传失败',
|
||||
reply: '回复',
|
||||
replyTo: '回复给',
|
||||
showMarkdown: '渲染',
|
||||
showRaw: '原文',
|
||||
allMembers: '全体成员',
|
||||
file: '文件',
|
||||
voice: '语音',
|
||||
uploadImage: '上传图片',
|
||||
uploading: '上传中...',
|
||||
},
|
||||
// Execution history and monitoring
|
||||
filterByDate: '按日期筛选',
|
||||
allTime: '全部时间',
|
||||
today: '今天',
|
||||
lastWeek: '最近一周',
|
||||
lastMonth: '最近一个月',
|
||||
showingExecutions: '显示 {{shown}} / {{total}} 条记录',
|
||||
rerun: '重新运行',
|
||||
rerunExecution: '重新执行',
|
||||
logs: '日志',
|
||||
details: '详情',
|
||||
completedAt: '完成时间',
|
||||
noNodeExecutions: '暂无节点执行记录',
|
||||
// Node config field labels (used by DynamicFormComponent)
|
||||
nodeConfigFields: {
|
||||
// trigger.py - MessageTriggerNode
|
||||
conditions: '触发条件',
|
||||
keyword_filter: '关键词过滤',
|
||||
regex_filter: '正则过滤',
|
||||
min_length: '最小长度',
|
||||
max_length: '最大长度',
|
||||
require_mention: '需要@机器人',
|
||||
respond_rules: '群响应规则',
|
||||
access_control: '访问控制',
|
||||
// trigger.py - CronTriggerNode
|
||||
cron: 'Cron表达式',
|
||||
timezone: '时区',
|
||||
description: '描述',
|
||||
// trigger.py - WebhookTriggerNode
|
||||
path: 'Webhook路径',
|
||||
allowed_methods: '允许的HTTP方法',
|
||||
content_type: 'Content-Type',
|
||||
auth_type: '认证方式',
|
||||
auth_key: '认证密钥',
|
||||
validation: '请求验证',
|
||||
timeout: '超时时间',
|
||||
// trigger.py - EventTriggerNode
|
||||
event_types: '事件类型',
|
||||
filter: '事件过滤',
|
||||
debounce_ms: '防抖时间',
|
||||
// process.py - LLMCallNode
|
||||
model: '模型',
|
||||
prompt_template: '提示词模板',
|
||||
system_prompt: '系统提示词',
|
||||
temperature: '温度',
|
||||
top_p: 'Top P',
|
||||
frequency_penalty: '频率惩罚',
|
||||
presence_penalty: '存在惩罚',
|
||||
max_tokens: '最大Token数',
|
||||
stop_sequences: '停止序列',
|
||||
seed: '随机种子',
|
||||
stream: '流式输出',
|
||||
use_conversation_history: '使用对话历史',
|
||||
// process.py - CodeExecutorNode
|
||||
language: '编程语言',
|
||||
code: '代码',
|
||||
// process.py - HTTPRequestNode
|
||||
url: '请求URL',
|
||||
method: '请求方法',
|
||||
auth_config: '认证配置',
|
||||
// process.py - DataTransformNode
|
||||
transform_type: '转换类型',
|
||||
template: '模板',
|
||||
expression: '表达式',
|
||||
output_type: '输出类型',
|
||||
// process.py - QuestionClassifierNode
|
||||
categories: '分类类别',
|
||||
instruction: '指令',
|
||||
// process.py - ParameterExtractorNode
|
||||
parameters: '参数定义',
|
||||
// process.py - KnowledgeRetrievalNode
|
||||
knowledge_bases: '知识库',
|
||||
top_k: '返回数量',
|
||||
score_threshold: '相似度阈值',
|
||||
search_method: '搜索方法',
|
||||
enable_citations: '启用引用',
|
||||
// control.py - ConditionNode
|
||||
condition_type: '条件类型',
|
||||
condition_expression: '条件表达式',
|
||||
left_value: '左值',
|
||||
operator: '比较运算符',
|
||||
right_value: '右值',
|
||||
// control.py - SwitchNode
|
||||
cases: '分支条件',
|
||||
// control.py - LoopNode
|
||||
max_iterations: '最大迭代次数',
|
||||
break_condition: '中断条件',
|
||||
// control.py - IteratorNode
|
||||
parallel: '并行处理',
|
||||
max_concurrency: '最大并发数',
|
||||
// control.py - ParallelNode
|
||||
branches: '分支配置',
|
||||
wait_all: '等待所有',
|
||||
fail_fast: '快速失败',
|
||||
// control.py - WaitNode
|
||||
duration: '等待时间',
|
||||
duration_type: '时间单位',
|
||||
// control.py - MergeNode
|
||||
merge_strategy: '合并策略',
|
||||
// control.py - VariableAggregatorNode
|
||||
variable_mappings: '变量映射',
|
||||
aggregation_mode: '聚合模式',
|
||||
// action.py - SendMessageNode
|
||||
target_type: '目标类型',
|
||||
target_id: '目标ID',
|
||||
platform: '平台',
|
||||
message_type: '消息类型',
|
||||
// action.py - ReplyMessageNode
|
||||
reply_mode: '回复模式',
|
||||
message_template: '消息模板',
|
||||
long_text_processing: '长文本处理',
|
||||
force_delay: '强制延迟',
|
||||
// action.py - CallPipelineNode
|
||||
pipeline_uuid: '流水线',
|
||||
inherit_context: '继承上下文',
|
||||
// action.py - StoreDataNode
|
||||
storage_type: '存储类型',
|
||||
ttl: '过期时间',
|
||||
key_prefix: '键前缀',
|
||||
// action.py - SetVariableNode
|
||||
variable_name: '变量名称',
|
||||
variable_scope: '变量作用域',
|
||||
set_variable_operation: '操作类型',
|
||||
// action.py - OpeningStatementNode
|
||||
statement: '开场白',
|
||||
suggested_questions: '建议问题',
|
||||
show_suggestions: '显示建议',
|
||||
// action.py - EndNode
|
||||
output_format: '输出格式',
|
||||
success_message: '成功消息',
|
||||
// integration.py - DatabaseQueryNode
|
||||
connection_type: '数据库类型',
|
||||
connection_string: '连接字符串',
|
||||
query: 'SQL查询',
|
||||
query_type: '查询类型',
|
||||
// integration.py - RedisOperationNode
|
||||
connection_url: '连接URL',
|
||||
operation: '操作类型',
|
||||
key_template: '键模板',
|
||||
hash_field: '哈希字段',
|
||||
// integration.py - MCPToolNode
|
||||
server_name: '服务器名称',
|
||||
tool_name: '工具名称',
|
||||
arguments_template: '参数模板',
|
||||
// integration.py - MemoryStoreNode
|
||||
scope: '作用域',
|
||||
// integration.py - DifyWorkflowNode
|
||||
'base-url': '基础URL',
|
||||
'api-key': 'API密钥',
|
||||
'app-type': '应用类型',
|
||||
// integration.py - DifyKnowledgeQueryNode
|
||||
dataset_id: '知识库ID',
|
||||
// integration.py - N8nWorkflowNode
|
||||
'webhook-url': 'Webhook URL',
|
||||
// integration.py - LangflowFlowNode
|
||||
'flow-id': '流程ID',
|
||||
// integration.py - CozeBotNode
|
||||
'bot-id': '机器人ID',
|
||||
'api-base': 'API基础URL',
|
||||
// process-configs.ts - TextTemplateNode
|
||||
escape_html: '转义HTML',
|
||||
trim_whitespace: '去除空白',
|
||||
// process-configs.ts - JsonTransformNode
|
||||
json_transform_type: 'JSON转换类型',
|
||||
json_expression: 'JSON表达式',
|
||||
mapping: '字段映射',
|
||||
// process-configs.ts - CodeExecutorNode
|
||||
code_language: '编程语言',
|
||||
code_content: '代码内容',
|
||||
// process-configs.ts - DataAggregatorNode
|
||||
aggregation_type: '聚合类型',
|
||||
separator: '分隔符',
|
||||
field_path: '字段路径',
|
||||
// process-configs.ts - TextSplitterNode
|
||||
split_type: '分割类型',
|
||||
chunk_size: '块大小',
|
||||
chunk_overlap: '块重叠',
|
||||
regex_pattern: '正则表达式',
|
||||
remove_empty: '移除空块',
|
||||
// process-configs.ts - VariableAssignmentNode
|
||||
assign_variable_name: '变量名',
|
||||
value_type: '值类型',
|
||||
static_value: '静态值',
|
||||
// integration-configs.ts - N8nWorkflowNode
|
||||
n8n_webhook_url: 'Webhook URL',
|
||||
n8n_auth_type: '认证方式',
|
||||
// integration-configs.ts - LangflowFlowNode
|
||||
langflow_flow_id: '流程ID',
|
||||
// integration-configs.ts - CozeBotNode
|
||||
coze_bot_id: '机器人ID',
|
||||
coze_api_base: 'API基础URL',
|
||||
},
|
||||
},
|
||||
265
web/workflows_translation_guide.md
Normal file
265
web/workflows_translation_guide.md
Normal file
@@ -0,0 +1,265 @@
|
||||
# Workflows翻译工作流程指南
|
||||
|
||||
## 📋 概述
|
||||
|
||||
本指南说明如何使用提供的工具完成workflows部分的多语言翻译工作。
|
||||
|
||||
## 🎯 翻译目标
|
||||
|
||||
- **总键数**: 627个workflows相关的键
|
||||
- **目标语言**: 6种语言(日语、繁体中文、西班牙语、俄语、泰语、越南语)
|
||||
- **总翻译项**: 3762个(627键 × 6语言)
|
||||
|
||||
## 🛠️ 工具说明
|
||||
|
||||
### 1. `workflows_translations.json` - 翻译模板文件
|
||||
|
||||
这是核心翻译文件,包含所有需要翻译的键和值。
|
||||
|
||||
**文件结构**:
|
||||
```json
|
||||
{
|
||||
"_comment": "Workflows翻译模板",
|
||||
"_instructions": [...],
|
||||
"_progress": {
|
||||
"total_keys": 627,
|
||||
"translated_keys": 0,
|
||||
"remaining_keys": 627,
|
||||
"languages": ["ja-JP", "zh-Hant", "es-ES", "ru-RU", "th-TH", "vi-VN"]
|
||||
},
|
||||
"translations": {
|
||||
"title": {
|
||||
"zh-Hans": "工作流对话",
|
||||
"ja-JP": "TODO",
|
||||
"zh-Hant": "TODO",
|
||||
"es-ES": "TODO",
|
||||
"ru-RU": "TODO",
|
||||
"th-TH": "TODO",
|
||||
"vi-VN": "TODO"
|
||||
},
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. `apply_workflows_translations.py` - 应用翻译脚本
|
||||
|
||||
将完成的翻译应用到实际的语言文件中。
|
||||
|
||||
**功能**:
|
||||
- 读取 `workflows_translations.json`
|
||||
- 识别已完成的翻译(非"TODO")
|
||||
- 应用到对应的语言文件(ja-JP.ts, zh-Hant.ts等)
|
||||
- 生成应用报告
|
||||
- 支持增量应用(可以分批翻译)
|
||||
|
||||
**使用方法**:
|
||||
```bash
|
||||
cd LangBot_copy/web
|
||||
python3 apply_workflows_translations.py
|
||||
```
|
||||
|
||||
### 3. `check_translation_progress.py` - 进度检查脚本
|
||||
|
||||
查看当前翻译进度和统计信息。
|
||||
|
||||
**功能**:
|
||||
- 显示每种语言的翻译进度
|
||||
- 显示进度条和百分比
|
||||
- 列出已完成和待完成的键
|
||||
- 建议下一批要翻译的键
|
||||
- 估算剩余工作量
|
||||
|
||||
**使用方法**:
|
||||
```bash
|
||||
cd LangBot_copy/web
|
||||
python3 check_translation_progress.py
|
||||
```
|
||||
|
||||
## 📝 翻译工作流程
|
||||
|
||||
### 步骤1: 检查当前进度
|
||||
|
||||
```bash
|
||||
python3 check_translation_progress.py
|
||||
```
|
||||
|
||||
这会显示:
|
||||
- 每种语言的完成度
|
||||
- 剩余待翻译的键数量
|
||||
- 建议下一批翻译的键
|
||||
|
||||
### 步骤2: 编辑翻译文件
|
||||
|
||||
打开 `workflows_translations.json` 文件,找到要翻译的键,将 `"TODO"` 替换为实际翻译。
|
||||
|
||||
**示例**:
|
||||
|
||||
翻译前:
|
||||
```json
|
||||
"title": {
|
||||
"zh-Hans": "工作流对话",
|
||||
"ja-JP": "TODO",
|
||||
"zh-Hant": "TODO",
|
||||
"es-ES": "TODO",
|
||||
"ru-RU": "TODO",
|
||||
"th-TH": "TODO",
|
||||
"vi-VN": "TODO"
|
||||
}
|
||||
```
|
||||
|
||||
翻译后:
|
||||
```json
|
||||
"title": {
|
||||
"zh-Hans": "工作流对话",
|
||||
"ja-JP": "ワークフロー会話",
|
||||
"zh-Hant": "工作流對話",
|
||||
"es-ES": "Conversación de Flujo de Trabajo",
|
||||
"ru-RU": "Диалог Рабочего Процесса",
|
||||
"th-TH": "การสนทนาเวิร์กโฟลว์",
|
||||
"vi-VN": "Hội thoại Quy trình"
|
||||
}
|
||||
```
|
||||
|
||||
**翻译技巧**:
|
||||
- 参考 `zh-Hans` 的中文原文理解含义
|
||||
- 保持专业术语的一致性
|
||||
- 注意特殊字符需要转义(如单引号 `'` 要写成 `\'`)
|
||||
- 可以使用翻译工具辅助,但需要人工审核确保准确性
|
||||
|
||||
### 步骤3: 应用翻译
|
||||
|
||||
完成一批翻译后,运行应用脚本:
|
||||
|
||||
```bash
|
||||
python3 apply_workflows_translations.py
|
||||
```
|
||||
|
||||
脚本会:
|
||||
- 自动识别已完成的翻译
|
||||
- 应用到对应的语言文件
|
||||
- 显示应用结果和统计信息
|
||||
- 更新进度信息
|
||||
|
||||
### 步骤4: 重复步骤1-3
|
||||
|
||||
继续翻译下一批键,直到所有翻译完成。
|
||||
|
||||
## 💡 建议的翻译策略
|
||||
|
||||
### 方案A: 按主题分批(推荐)
|
||||
|
||||
将相关的键分组翻译,例如:
|
||||
1. **第1批**: 基础UI文本(title, description, placeholder等)
|
||||
2. **第2批**: 节点相关(nodes, nodeTypes, nodeCategories等)
|
||||
3. **第3批**: 调试相关(debug, debugDialog, executionHistory等)
|
||||
4. **第4批**: 配置相关(nodeConfigFields, validation等)
|
||||
5. **第5批**: 其他剩余键
|
||||
|
||||
**优点**: 上下文相关,翻译更一致
|
||||
|
||||
### 方案B: 按数量分批
|
||||
|
||||
每批翻译固定数量的键(如50或100个)。
|
||||
|
||||
**优点**: 进度可控,容易估算时间
|
||||
|
||||
### 方案C: 按语言分批
|
||||
|
||||
先完成一种语言的所有翻译,再翻译下一种语言。
|
||||
|
||||
**优点**: 可以利用语言专长,集中精力
|
||||
|
||||
## 📊 进度跟踪
|
||||
|
||||
使用 `check_translation_progress.py` 随时查看进度:
|
||||
|
||||
```bash
|
||||
python3 check_translation_progress.py
|
||||
```
|
||||
|
||||
输出示例:
|
||||
```
|
||||
================================================================================
|
||||
📊 Workflows翻译进度报告
|
||||
================================================================================
|
||||
|
||||
【日语 (ja-JP)】
|
||||
[████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 20.0%
|
||||
✅ 已完成: 125/627
|
||||
⏳ 待翻译: 502
|
||||
📝 最近完成: title, description, placeholder, ...
|
||||
|
||||
【繁体中文 (zh-Hant)】
|
||||
[████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 20.0%
|
||||
✅ 已完成: 125/627
|
||||
⏳ 待翻译: 502
|
||||
|
||||
...
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
📈 总体进度: 750/3762 (19.9%)
|
||||
📊 平均每种语言: 125/627
|
||||
|
||||
💡 剩余工作量: 3012 个翻译项
|
||||
建议分批策略:
|
||||
- 每批50个键: 需要 61 批
|
||||
- 每批100个键: 需要 31 批
|
||||
================================================================================
|
||||
```
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
1. **备份**: 在开始翻译前,建议备份原始文件
|
||||
2. **编码**: 确保使用UTF-8编码保存文件
|
||||
3. **格式**: 保持JSON格式正确,注意逗号和引号
|
||||
4. **特殊字符**: 单引号需要转义为 `\'`
|
||||
5. **验证**: 每次应用翻译后,检查语言文件是否有语法错误
|
||||
6. **增量工作**: 可以分多次完成,每次翻译一部分然后应用
|
||||
|
||||
## 🔍 故障排除
|
||||
|
||||
### 问题1: 应用翻译时提示"没有发现任何已完成的翻译"
|
||||
|
||||
**原因**: 所有值都还是 "TODO"
|
||||
|
||||
**解决**: 编辑 `workflows_translations.json`,将至少一个键的 "TODO" 替换为实际翻译
|
||||
|
||||
### 问题2: 应用翻译后语言文件报错
|
||||
|
||||
**原因**: 翻译中包含未转义的特殊字符
|
||||
|
||||
**解决**: 检查翻译文本,将单引号 `'` 替换为 `\'`
|
||||
|
||||
### 问题3: 某些键应用失败
|
||||
|
||||
**原因**: 键名在语言文件中不存在或格式不匹配
|
||||
|
||||
**解决**: 检查应用报告中的失败键列表,手动在语言文件中添加或修正
|
||||
|
||||
## 📈 预估工作量
|
||||
|
||||
- **总翻译项**: 3762个
|
||||
- **每小时翻译速度**: 约50-100项(取决于熟练度)
|
||||
- **预估总时间**: 38-75小时
|
||||
- **建议分批**: 每天翻译100-200项,约需19-38天完成
|
||||
|
||||
## ✅ 完成标准
|
||||
|
||||
当 `check_translation_progress.py` 显示:
|
||||
- 所有语言的进度都达到 100%
|
||||
- 没有剩余待翻译的键
|
||||
- 所有语言文件都能正常编译
|
||||
|
||||
则翻译工作完成!
|
||||
|
||||
## 🎉 完成后
|
||||
|
||||
1. 运行最后一次 `apply_workflows_translations.py` 确保所有翻译已应用
|
||||
2. 测试各语言界面,确认翻译正确显示
|
||||
3. 提交代码变更
|
||||
4. 生成最终翻译报告
|
||||
|
||||
---
|
||||
|
||||
**祝翻译工作顺利!** 🚀
|
||||
5676
web/workflows_translations.json
Normal file
5676
web/workflows_translations.json
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user