From c8a1bd5cbf7190941dc23eb53db59cb43fb5e012 Mon Sep 17 00:00:00 2001 From: ghostubborn Date: Wed, 1 Apr 2026 15:20:49 +0800 Subject: [PATCH 01/33] feat(i18n): add shared translation files and language registry --- locales/en.json | 338 +++++++++++++++++++++++++++++++++++++++++ locales/languages.json | 30 ++++ locales/zh.json | 338 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 706 insertions(+) create mode 100644 locales/en.json create mode 100644 locales/languages.json create mode 100644 locales/zh.json diff --git a/locales/en.json b/locales/en.json new file mode 100644 index 000000000..0a506e2c5 --- /dev/null +++ b/locales/en.json @@ -0,0 +1,338 @@ +{ + "common": { + "confirm": "Confirm", + "cancel": "Cancel", + "loading": "Loading...", + "error": "Error", + "success": "Success", + "completed": "Completed", + "processing": "Generating", + "pending": "Pending", + "ready": "Ready", + "running": "Running", + "failed": "Failed", + "unknown": "Unknown", + "unknownError": "Unknown error", + "none": "None", + "close": "Close", + "back": "Back", + "next": "Next", + "retry": "Retry", + "noData": "No data available", + "hours": "hours", + "minutes": "minutes", + "rounds": "rounds", + "items": "items", + "files": "files" + }, + "meta": { + "title": "MiroFish - Predict Everything", + "description": "MiroFish - Social Media Opinion Simulation System" + }, + "nav": { + "visitGithub": "Visit our Github page" + }, + "home": { + "tagline": "Concise & Universal Swarm Intelligence Engine", + "version": "/ v0.1-Preview", + "heroTitle1": "Upload Any Report", + "heroTitle2": "Simulate the Future Instantly", + "heroDesc": "Even from a single paragraph, {brand} can extract reality seeds and auto-generate a parallel world of up to {agentScale}. Inject variables from a god's-eye view to find the {optimalSolution} amid complex group interactions in dynamic environments.", + "heroDescBrand": "MiroFish", + "heroDescAgentScale": "millions of Agents", + "heroDescOptimalSolution": "\"local optimum\"", + "slogan": "Let the future rehearse among Agents, let decisions prevail after a hundred battles", + "systemStatus": "System Status", + "systemReady": "Ready", + "systemReadyDesc": "Prediction engine on standby. Upload unstructured data to initialize a simulation sequence.", + "metricLowCost": "Low Cost", + "metricLowCostDesc": "Avg. $5 per simulation", + "metricHighAvail": "High Availability", + "metricHighAvailDesc": "Up to millions of Agents", + "workflowSequence": "Workflow Sequence", + "step01Title": "Graph Build", + "step01Desc": "Reality seed extraction & individual/group memory injection & GraphRAG construction", + "step02Title": "Environment Setup", + "step02Desc": "Entity-relation extraction & persona generation & environment config with Agent injection", + "step03Title": "Run Simulation", + "step03Desc": "Dual-platform parallel simulation & auto-parse prediction requirements & dynamic temporal memory", + "step04Title": "Report Generation", + "step04Desc": "ReportAgent with a rich toolset interacts deeply with the post-simulation environment", + "step05Title": "Deep Interaction", + "step05Desc": "Chat with any individual in the simulated world & converse with ReportAgent", + "realitySeed": "01 / Reality Seed", + "supportedFormats": "Formats: PDF, MD, TXT", + "dragToUpload": "Drag files to upload", + "orBrowse": "or click to browse files", + "inputParams": "Input Parameters", + "simulationPrompt": ">_ 02 / Simulation Prompt", + "promptPlaceholder": "// Describe your simulation or prediction requirement in natural language", + "engineBadge": "Engine: MiroFish-V1.0", + "startEngine": "Start Engine", + "initializing": "Initializing..." + }, + "main": { + "layoutGraph": "Graph", + "layoutSplit": "Split", + "layoutWorkbench": "Workbench", + "stepNames": ["Graph Build", "Env Setup", "Run Simulation", "Report Generation", "Deep Interaction"] + }, + "step1": { + "ontologyGeneration": "Ontology Generation", + "ontologyCompleted": "Completed", + "ontologyGenerating": "Generating", + "ontologyPending": "Pending", + "ontologyDesc": "LLM analyzes document content and simulation requirements, extracts reality seeds, and auto-generates a suitable ontology structure", + "analyzingDocs": "Analyzing documents...", + "graphRagBuild": "GraphRAG Build", + "graphRagDesc": "Based on the generated ontology, documents are auto-chunked and sent to Zep to build a knowledge graph, extracting entities and relations, forming temporal memory and community summaries", + "entityNodes": "Entity Nodes", + "relationEdges": "Relation Edges", + "schemaTypes": "Schema Types", + "buildComplete": "Build Complete", + "buildCompleteDesc": "Graph build is complete. Proceed to the next step for simulation environment setup.", + "inProgress": "In Progress", + "creating": "Creating...", + "enterEnvSetup": "Enter Environment Setup", + "createSimulationFailed": "Failed to create simulation: {error}", + "createSimulationException": "Simulation creation error: {error}" + }, + "step2": { + "simInstanceInit": "Simulation Instance Initialization", + "simInstanceDesc": "Create a new simulation instance and pull world parameter templates", + "asyncTaskDone": "Async task completed", + "generateAgentPersona": "Generate Agent Personas", + "generateAgentPersonaDesc": "Combine context to auto-extract entities and relations from the knowledge graph, initialize simulated individuals, and assign unique behaviors and memories based on reality seeds", + "currentAgentCount": "Current Agents", + "expectedAgentTotal": "Expected Total Agents", + "relatedTopicsCount": "Reality Seed Related Topics", + "generatedAgentPersonas": "Generated Agent Personas", + "unknownProfession": "Unknown profession", + "noBio": "No bio available", + "dualPlatformConfig": "Generate Dual-Platform Config", + "dualPlatformConfigDesc": "LLM intelligently sets world time flow, recommendation algorithms, each individual's active hours, posting frequency, event triggers, and more based on requirements and reality seeds", + "simulationDuration": "Simulation Duration", + "roundDuration": "Round Duration", + "totalRounds": "Total Rounds", + "activePerHour": "Active Per Hour", + "peakHours": "Peak Hours", + "workHours": "Work Hours", + "morningHours": "Morning Hours", + "offPeakHours": "Off-Peak Hours", + "agentConfig": "Agent Config", + "activeTimePeriod": "Active Hours", + "postsPerHour": "Posts/hr", + "commentsPerHour": "Comments/hr", + "responseDelay": "Response Delay", + "activityLevel": "Activity Level", + "sentimentBias": "Sentiment Bias", + "influenceWeight": "Influence", + "recommendAlgoConfig": "Recommendation Algorithm Config", + "platform1Name": "Platform 1: Plaza / Feed", + "platform2Name": "Platform 2: Topic / Community", + "recencyWeight": "Recency Weight", + "popularityWeight": "Popularity Weight", + "relevanceWeight": "Relevance Weight", + "viralThreshold": "Viral Threshold", + "echoChamberStrength": "Echo Chamber Strength", + "llmConfigReasoning": "LLM Config Reasoning", + "initialActivation": "Initial Activation Orchestration", + "initialActivationDesc": "Auto-generate initial activation events and hot topics based on narrative direction to guide the simulation world's initial state", + "orchestrating": "Orchestrating", + "narrativeDirection": "Narrative Direction", + "initialHotTopics": "Initial Hot Topics", + "initialActivationSeq": "Initial Activation Sequence ({count})", + "setupComplete": "Setup Complete", + "setupCompleteDesc": "Simulation environment is ready. You can now start the simulation.", + "roundsConfig": "Simulation Rounds Configuration", + "roundsConfigDesc": "MiroFish auto-plans to simulate {hours} real-world hours, each round representing {minutesPerRound} minutes of elapsed time", + "customToggle": "Custom", + "roundsUnit": "rounds", + "estimatedDuration": "For 100 Agents: est. ~{minutes} minutes", + "estimatedDurationFull": "For 100 Agents: est. {minutes} minutes", + "recommendedRounds": "{rounds} (recommended)", + "customTip": "For first-time runs, we strongly recommend switching to 'Custom Mode' to reduce rounds for a quick preview and lower error risk", + "backToGraphBuild": "Back to Graph Build", + "startDualWorldSim": "Start Dual-World Parallel Simulation", + "profileModalAge": "Apparent Age", + "profileModalGender": "Apparent Gender", + "profileModalCountry": "Country/Region", + "profileModalMbti": "Apparent MBTI", + "profileModalBio": "Persona Bio", + "profileModalTopics": "Reality Seed Related Topics", + "profileModalPersona": "Detailed Persona Background", + "personaDimExperience": "Full Event Experience", + "personaDimExperienceDesc": "Complete behavioral trajectory in this event", + "personaDimBehavior": "Behavioral Profile", + "personaDimBehaviorDesc": "Experience summary and behavioral preferences", + "personaDimMemory": "Unique Memory Imprint", + "personaDimMemoryDesc": "Memories formed from reality seeds", + "personaDimSocial": "Social Network", + "personaDimSocialDesc": "Individual connections and interaction graph", + "genderMale": "Male", + "genderFemale": "Female", + "genderOther": "Other", + "yearsOld": "years old" + }, + "step3": { + "startGenerateReport": "Generate Result Report", + "generatingReport": "Starting...", + "waitingForActions": "Waiting for agent actions...", + "errorMissingSimId": "Error: missing simulationId", + "startingDualSim": "Starting dual-platform parallel simulation...", + "graphMemoryUpdateEnabled": "Dynamic graph memory update enabled", + "setMaxRounds": "Max simulation rounds set to: {rounds}", + "oldSimCleared": "Old simulation logs cleared, restarting simulation", + "engineStarted": "Simulation engine started successfully", + "startFailed": "Start failed: {error}", + "startException": "Start error: {error}", + "stoppingSim": "Stopping simulation...", + "simStopped": "Simulation stopped", + "stopFailed": "Stop failed: {error}", + "stopException": "Stop error: {error}", + "allPlatformsCompleted": "All platform simulations have ended", + "simCompleted": "Simulation completed", + "graphRealtimeRefresh": "Graph real-time refresh enabled (30s)", + "graphRefreshStopped": "Graph real-time refresh stopped", + "preparingGoBack": "Preparing to return to Step 2, closing simulation...", + "closingSimEnv": "Closing simulation environment...", + "simEnvClosed": "Simulation environment closed", + "closeFailed": "Failed to close simulation environment, attempting force stop...", + "stoppingProcess": "Stopping simulation process...", + "checkStatusFailed": "Failed to check simulation status: {error}", + "forceStopSuccess": "Simulation force stopped", + "forceStopFailed": "Force stop failed: {error}" + }, + "step4": { + "generatingSection": "Generating {title}...", + "goToInteraction": "Enter Deep Interaction", + "waitingForReportAgent": "Waiting for Report Agent..." + }, + "step5": { + "interactiveTools": "Interactive Tools", + "agentsAvailable": "{count} agents available", + "chatWithReportAgent": "Chat with Report Agent", + "chatWithAgent": "Chat with any individual in the world", + "selectChatTarget": "Select chat target", + "sendSurvey": "Send survey to the world", + "reportAgentChat": "Report Agent - Chat", + "reportAgentDesc": "A conversational version of the report generation agent with access to 4 professional tools and MiroFish's complete memory", + "toolInsightForge": "InsightForge Deep Attribution", + "toolInsightForgeDesc": "Aligns real-world seed data with simulation state, combining Global/Local Memory for cross-temporal deep attribution analysis", + "toolPanoramaSearch": "PanoramaSearch Full Tracking", + "toolPanoramaSearchDesc": "Graph-based BFS algorithm that reconstructs event propagation paths, capturing the full topology of information flow", + "toolQuickSearch": "QuickSearch Fast Retrieval", + "toolQuickSearchDesc": "GraphRAG-based instant query interface with optimized indexing for fast extraction of node attributes and discrete facts", + "toolInterviewSubAgent": "InterviewSubAgent Virtual Interview", + "toolInterviewSubAgentDesc": "Autonomous interviews that conduct parallel multi-round dialogues with simulated individuals, collecting unstructured opinion data and psychological states", + "profileBio": "Bio", + "chatEmptyReportAgent": "Chat with Report Agent to explore report content in depth", + "chatEmptyAgent": "Chat with simulated individuals to understand their perspectives", + "chatInputPlaceholder": "Type your question...", + "selectSurveyTarget": "Select survey targets", + "selectedCount": "Selected {selected} / {total}", + "surveyQuestions": "Survey Questions", + "surveyInputPlaceholder": "Enter the question you want to ask all selected targets...", + "submitSurvey": "Send Survey", + "surveyResults": "Survey Results", + "surveyResultsCount": "{count} responses", + "selectAll": "Select All", + "clearSelection": "Clear" + }, + "graph": { + "panelTitle": "Graph Relationship Visualization", + "refreshGraph": "Refresh Graph", + "graphMemoryRealtime": "GraphRAG short/long-term memory updating in real-time", + "realtimeUpdating": "Updating in real-time...", + "pendingContentHint": "Some content is still processing. Consider refreshing the graph manually later.", + "nodeDetails": "Node Details", + "relationship": "Relationship" + }, + "history": { + "title": "Simulation History", + "graphBuild": "Graph Build", + "envSetup": "Env Setup", + "analysisReport": "Analysis Report", + "moreFiles": "+{count} files", + "noFiles": "No files", + "loadingText": "Loading...", + "simRequirement": "Simulation Requirement", + "relatedFiles": "Related Files", + "noRelatedFiles": "No related files", + "replayTitle": "Simulation Replay", + "step1Button": "Graph Build", + "step2Button": "Env Setup", + "step4Button": "Analysis Report", + "replayHint": "Step 3 'Run Simulation' and Step 5 'Deep Interaction' must be started during runtime and do not support history replay" + }, + "api": { + "projectNotFound": "Project not found: {id}", + "projectDeleteFailed": "Project not found or deletion failed: {id}", + "projectDeleted": "Project deleted: {id}", + "projectReset": "Project reset: {id}", + "requireSimulationRequirement": "Please provide a simulation requirement (simulation_requirement)", + "requireFileUpload": "Please upload at least one document file", + "noDocProcessed": "No documents were processed successfully. Please check file formats.", + "requireProjectId": "Please provide project_id", + "configError": "Configuration error: {details}", + "zepApiKeyMissing": "ZEP_API_KEY not configured", + "ontologyNotGenerated": "Ontology not yet generated. Please call /ontology/generate first.", + "graphBuilding": "Graph build in progress. Do not resubmit. To force rebuild, add force: true.", + "textNotFound": "Extracted text content not found", + "ontologyNotFound": "Ontology definition not found", + "graphBuildStarted": "Graph build task started. Query progress via /task/{taskId}.", + "graphBuildComplete": "Graph build complete", + "buildFailed": "Build failed: {error}", + "taskNotFound": "Task not found: {id}", + "graphDeleted": "Graph deleted: {id}", + "entityNotFound": "Entity not found: {id}", + "graphNotBuilt": "Graph not yet built. Please call /api/graph/build first.", + "requireSimulationId": "Please provide simulation_id", + "simulationNotFound": "Simulation not found: {id}", + "projectMissingRequirement": "Project missing simulation requirement (simulation_requirement)", + "prepareStarted": "Preparation task started. Query progress via /api/simulation/prepare/status.", + "alreadyPrepared": "Preparation already complete. No need to regenerate.", + "notStartedPrepare": "Preparation not started. Please call /api/simulation/prepare.", + "taskCompletedPrepared": "Task completed (preparation already exists)", + "requireTaskOrSimId": "Please provide task_id or simulation_id", + "configNotFound": "Simulation config not found. Please call /prepare first.", + "configFileNotFound": "Config file not found. Please call /prepare first.", + "unknownScript": "Unknown script: {name}. Available: {allowed}", + "scriptFileNotFound": "Script file not found: {name}", + "requireGraphId": "Please provide graph_id", + "noMatchingEntities": "No matching entities found", + "maxRoundsPositive": "max_rounds must be a positive integer", + "maxRoundsInvalid": "max_rounds must be a valid integer", + "invalidPlatform": "Invalid platform type: {platform}. Options: twitter/reddit/parallel", + "simRunningForceHint": "Simulation is running. Stop it first via /stop, or use force=true to restart.", + "simNotReady": "Simulation not ready. Current status: {status}. Please call /prepare first.", + "graphIdRequiredForMemory": "Graph memory update requires a valid graph_id. Ensure the graph is built.", + "dbNotExist": "Database does not exist. The simulation may not have run yet.", + "requireMessage": "Please provide a message", + "missingGraphId": "Missing graph ID", + "missingGraphIdEnsure": "Missing graph ID. Please ensure the graph has been built.", + "missingSimRequirement": "Missing simulation requirement description", + "reportAlreadyExists": "Report already exists", + "reportGenerateStarted": "Report generation task started. Query progress via /api/report/generate/status.", + "reportGenerated": "Report generated", + "reportNotFound": "Report not found: {id}", + "noReportForSim": "No report found for this simulation: {id}", + "reportDeleted": "Report deleted: {id}", + "reportGenerateFailed": "Report generation failed", + "sectionNotFound": "Section not found: section_{index}.md", + "reportProgressNotAvail": "Report not found or progress unavailable: {id}", + "requireAgentId": "Please provide agent_id", + "requirePrompt": "Please provide a prompt (interview question)", + "invalidInterviewPlatform": "Platform must be either 'twitter' or 'reddit'", + "envNotRunning": "Simulation environment not running or closed. Ensure simulation is complete and in command-wait mode.", + "interviewTimeout": "Interview response timed out: {error}", + "requireInterviews": "Please provide interviews (interview list)", + "interviewListMissingAgentId": "Interview list item {index} missing agent_id", + "interviewListMissingPrompt": "Interview list item {index} missing prompt", + "interviewListInvalidPlatform": "Interview list item {index} platform must be 'twitter' or 'reddit'", + "batchInterviewTimeout": "Batch interview response timed out: {error}", + "globalInterviewTimeout": "Global interview response timed out: {error}", + "envRunning": "Environment is running and ready for Interview commands", + "initReportAgent": "Initializing Report Agent..." + } +} diff --git a/locales/languages.json b/locales/languages.json new file mode 100644 index 000000000..81732cec8 --- /dev/null +++ b/locales/languages.json @@ -0,0 +1,30 @@ +{ + "zh": { + "label": "中文", + "llmInstruction": "请使用中文回答。" + }, + "en": { + "label": "English", + "llmInstruction": "Please respond in English." + }, + "es": { + "label": "Español", + "llmInstruction": "Por favor, responde en español." + }, + "fr": { + "label": "Français", + "llmInstruction": "Veuillez répondre en français." + }, + "pt": { + "label": "Português", + "llmInstruction": "Por favor, responda em português." + }, + "ru": { + "label": "Русский", + "llmInstruction": "Пожалуйста, отвечайте на русском языке." + }, + "de": { + "label": "Deutsch", + "llmInstruction": "Bitte antworten Sie auf Deutsch." + } +} diff --git a/locales/zh.json b/locales/zh.json new file mode 100644 index 000000000..aa4879922 --- /dev/null +++ b/locales/zh.json @@ -0,0 +1,338 @@ +{ + "common": { + "confirm": "确认", + "cancel": "取消", + "loading": "加载中...", + "error": "错误", + "success": "成功", + "completed": "已完成", + "processing": "生成中", + "pending": "等待", + "ready": "就绪", + "running": "运行中", + "failed": "失败", + "unknown": "未知", + "unknownError": "未知错误", + "none": "无", + "close": "关闭", + "back": "返回", + "next": "下一步", + "retry": "重试", + "noData": "暂无数据", + "hours": "小时", + "minutes": "分钟", + "rounds": "轮", + "items": "个", + "files": "个文件" + }, + "meta": { + "title": "MiroFish - 预测万物", + "description": "MiroFish - 社交媒体舆论模拟系统" + }, + "nav": { + "visitGithub": "访问我们的Github主页" + }, + "home": { + "tagline": "简洁通用的群体智能引擎", + "version": "/ v0.1-预览版", + "heroTitle1": "上传任意报告", + "heroTitle2": "即刻推演未来", + "heroDesc": "即使只有一段文字,{brand} 也能基于其中的现实种子,全自动生成与之对应的至多{agentScale}构成的平行世界。通过上帝视角注入变量,在复杂的群体交互中寻找动态环境下的{optimalSolution}", + "heroDescBrand": "MiroFish", + "heroDescAgentScale": "百万级Agent", + "heroDescOptimalSolution": "\"局部最优解\"", + "slogan": "让未来在 Agent 群中预演,让决策在百战后胜出", + "systemStatus": "系统状态", + "systemReady": "准备就绪", + "systemReadyDesc": "预测引擎待命中,可上传多份非结构化数据以初始化模拟序列", + "metricLowCost": "低成本", + "metricLowCostDesc": "常规模拟平均5$/次", + "metricHighAvail": "高可用", + "metricHighAvailDesc": "最多百万级Agent模拟", + "workflowSequence": "工作流序列", + "step01Title": "图谱构建", + "step01Desc": "现实种子提取 & 个体与群体记忆注入 & GraphRAG构建", + "step02Title": "环境搭建", + "step02Desc": "实体关系抽取 & 人设生成 & 环境配置Agent注入仿真参数", + "step03Title": "开始模拟", + "step03Desc": "双平台并行模拟 & 自动解析预测需求 & 动态更新时序记忆", + "step04Title": "报告生成", + "step04Desc": "ReportAgent拥有丰富的工具集与模拟后环境进行深度交互", + "step05Title": "深度互动", + "step05Desc": "与模拟世界中的任意一位进行对话 & 与ReportAgent进行对话", + "realitySeed": "01 / 现实种子", + "supportedFormats": "支持格式: PDF, MD, TXT", + "dragToUpload": "拖拽文件上传", + "orBrowse": "或点击浏览文件系统", + "inputParams": "输入参数", + "simulationPrompt": ">_ 02 / 模拟提示词", + "promptPlaceholder": "// 用自然语言输入模拟或预测需求(例.武大若发布撤销肖某处分的公告,会引发什么舆情走向)", + "engineBadge": "引擎: MiroFish-V1.0", + "startEngine": "启动引擎", + "initializing": "初始化中..." + }, + "main": { + "layoutGraph": "图谱", + "layoutSplit": "双栏", + "layoutWorkbench": "工作台", + "stepNames": ["图谱构建", "环境搭建", "开始模拟", "报告生成", "深度互动"] + }, + "step1": { + "ontologyGeneration": "本体生成", + "ontologyCompleted": "已完成", + "ontologyGenerating": "生成中", + "ontologyPending": "等待", + "ontologyDesc": "LLM分析文档内容与模拟需求,提取出现实种子,自动生成合适的本体结构", + "analyzingDocs": "正在分析文档...", + "graphRagBuild": "GraphRAG构建", + "graphRagDesc": "基于生成的本体,将文档自动分块后调用 Zep 构建知识图谱,提取实体和关系,并形成时序记忆与社区摘要", + "entityNodes": "实体节点", + "relationEdges": "关系边", + "schemaTypes": "SCHEMA类型", + "buildComplete": "构建完成", + "buildCompleteDesc": "图谱构建已完成,请进入下一步进行模拟环境搭建", + "inProgress": "进行中", + "creating": "创建中...", + "enterEnvSetup": "进入环境搭建", + "createSimulationFailed": "创建模拟失败: {error}", + "createSimulationException": "创建模拟异常: {error}" + }, + "step2": { + "simInstanceInit": "模拟实例初始化", + "simInstanceDesc": "新建simulation实例,拉取模拟世界参数模版", + "asyncTaskDone": "异步任务已完成", + "generateAgentPersona": "生成 Agent 人设", + "generateAgentPersonaDesc": "结合上下文,自动调用工具从知识图谱梳理实体与关系,初始化模拟个体,并基于现实种子赋予他们独特的行为与记忆", + "currentAgentCount": "当前Agent数", + "expectedAgentTotal": "预期Agent总数", + "relatedTopicsCount": "现实种子当前关联话题数", + "generatedAgentPersonas": "已生成的 Agent 人设", + "unknownProfession": "未知职业", + "noBio": "暂无简介", + "dualPlatformConfig": "生成双平台模拟配置", + "dualPlatformConfigDesc": "LLM 根据模拟需求与现实种子,智能设置世界时间流速、推荐算法、每个个体的活跃时间段、发言频率、事件触发等参数", + "simulationDuration": "模拟时长", + "roundDuration": "每轮时长", + "totalRounds": "总轮次", + "activePerHour": "每小时活跃", + "peakHours": "高峰时段", + "workHours": "工作时段", + "morningHours": "早间时段", + "offPeakHours": "低谷时段", + "agentConfig": "Agent 配置", + "activeTimePeriod": "活跃时段", + "postsPerHour": "发帖/时", + "commentsPerHour": "评论/时", + "responseDelay": "响应延迟", + "activityLevel": "活跃度", + "sentimentBias": "情感倾向", + "influenceWeight": "影响力", + "recommendAlgoConfig": "推荐算法配置", + "platform1Name": "平台 1:广场 / 信息流", + "platform2Name": "平台 2:话题 / 社区", + "recencyWeight": "时效权重", + "popularityWeight": "热度权重", + "relevanceWeight": "相关性权重", + "viralThreshold": "病毒阈值", + "echoChamberStrength": "回音室强度", + "llmConfigReasoning": "LLM 配置推理", + "initialActivation": "初始激活编排", + "initialActivationDesc": "基于叙事方向,自动生成初始激活事件与热点话题,引导模拟世界的初始状态", + "orchestrating": "编排中", + "narrativeDirection": "叙事引导方向", + "initialHotTopics": "初始热点话题", + "initialActivationSeq": "初始激活序列 ({count})", + "setupComplete": "准备完成", + "setupCompleteDesc": "模拟环境已准备完成,可以开始运行模拟", + "roundsConfig": "模拟轮数设定", + "roundsConfigDesc": "MiroFish 自动规划推演现实 {hours} 小时,每轮代表现实 {minutesPerRound} 分钟时间流逝", + "customToggle": "自定义", + "roundsUnit": "轮", + "estimatedDuration": "若Agent规模为100:预计耗时约 {minutes} 分钟", + "estimatedDurationFull": "若Agent规模为100:预计耗时 {minutes} 分钟", + "recommendedRounds": "{rounds} (推荐)", + "customTip": "若首次运行,强烈建议切换至'自定义模式'减少模拟轮数,以便快速预览效果并降低报错风险", + "backToGraphBuild": "返回图谱构建", + "startDualWorldSim": "开始双世界并行模拟", + "profileModalAge": "事件外显年龄", + "profileModalGender": "事件外显性别", + "profileModalCountry": "国家/地区", + "profileModalMbti": "事件外显MBTI", + "profileModalBio": "人设简介", + "profileModalTopics": "现实种子关联话题", + "profileModalPersona": "详细人设背景", + "personaDimExperience": "事件全景经历", + "personaDimExperienceDesc": "在此事件中的完整行为轨迹", + "personaDimBehavior": "行为模式侧写", + "personaDimBehaviorDesc": "经验总结与行事风格偏好", + "personaDimMemory": "独特记忆印记", + "personaDimMemoryDesc": "基于现实种子形成的记忆", + "personaDimSocial": "社会关系网络", + "personaDimSocialDesc": "个体链接与交互图谱", + "genderMale": "男", + "genderFemale": "女", + "genderOther": "其他", + "yearsOld": "岁" + }, + "step3": { + "startGenerateReport": "开始生成结果报告", + "generatingReport": "启动中...", + "waitingForActions": "Waiting for agent actions...", + "errorMissingSimId": "错误:缺少 simulationId", + "startingDualSim": "正在启动双平台并行模拟...", + "graphMemoryUpdateEnabled": "已开启动态图谱更新模式", + "setMaxRounds": "设置最大模拟轮数: {rounds}", + "oldSimCleared": "已清理旧的模拟日志,重新开始模拟", + "engineStarted": "模拟引擎启动成功", + "startFailed": "启动失败: {error}", + "startException": "启动异常: {error}", + "stoppingSim": "正在停止模拟...", + "simStopped": "模拟已停止", + "stopFailed": "停止失败: {error}", + "stopException": "停止异常: {error}", + "allPlatformsCompleted": "检测到所有平台模拟已结束", + "simCompleted": "模拟已完成", + "graphRealtimeRefresh": "开启图谱实时刷新 (30s)", + "graphRefreshStopped": "停止图谱实时刷新", + "preparingGoBack": "准备返回 Step 2,正在关闭模拟...", + "closingSimEnv": "正在关闭模拟环境...", + "simEnvClosed": "模拟环境已关闭", + "closeFailed": "关闭模拟环境失败,尝试强制停止...", + "stoppingProcess": "正在停止模拟进程...", + "checkStatusFailed": "检查模拟状态失败: {error}", + "forceStopSuccess": "模拟已强制停止", + "forceStopFailed": "强制停止失败: {error}" + }, + "step4": { + "generatingSection": "正在生成{title}...", + "goToInteraction": "进入深度互动", + "waitingForReportAgent": "Waiting for Report Agent..." + }, + "step5": { + "interactiveTools": "Interactive Tools", + "agentsAvailable": "{count} agents available", + "chatWithReportAgent": "与Report Agent对话", + "chatWithAgent": "与世界中任意个体对话", + "selectChatTarget": "选择对话对象", + "sendSurvey": "发送问卷调查到世界中", + "reportAgentChat": "Report Agent - Chat", + "reportAgentDesc": "报告生成智能体的快速对话版本,可调用 4 种专业工具,拥有MiroFish的完整记忆", + "toolInsightForge": "InsightForge 深度归因", + "toolInsightForgeDesc": "对齐现实世界种子数据与模拟环境状态,结合Global/Local Memory机制,提供跨时空的深度归因分析", + "toolPanoramaSearch": "PanoramaSearch 全景追踪", + "toolPanoramaSearchDesc": "基于图结构的广度遍历算法,重构事件传播路径,捕获全量信息流动的拓扑结构", + "toolQuickSearch": "QuickSearch 快速检索", + "toolQuickSearchDesc": "基于 GraphRAG 的即时查询接口,优化索引效率,用于快速提取具体的节点属性与离散事实", + "toolInterviewSubAgent": "InterviewSubAgent 虚拟访谈", + "toolInterviewSubAgentDesc": "自主式访谈,能够并行与模拟世界中个体进行多轮对话,采集非结构化的观点数据与心理状态", + "profileBio": "简介", + "chatEmptyReportAgent": "与 Report Agent 对话,深入了解报告内容", + "chatEmptyAgent": "与模拟个体对话,了解他们的观点", + "chatInputPlaceholder": "输入您的问题...", + "selectSurveyTarget": "选择调查对象", + "selectedCount": "已选 {selected} / {total}", + "surveyQuestions": "问卷问题", + "surveyInputPlaceholder": "输入您想问所有被选中对象的问题...", + "submitSurvey": "发送问卷", + "surveyResults": "调查结果", + "surveyResultsCount": "{count} 条回复", + "selectAll": "全选", + "clearSelection": "清空" + }, + "graph": { + "panelTitle": "Graph Relationship Visualization", + "refreshGraph": "刷新图谱", + "graphMemoryRealtime": "GraphRAG长短期记忆实时更新中", + "realtimeUpdating": "实时更新中...", + "pendingContentHint": "还有少量内容处理中,建议稍后手动刷新图谱", + "nodeDetails": "Node Details", + "relationship": "Relationship" + }, + "history": { + "title": "推演记录", + "graphBuild": "图谱构建", + "envSetup": "环境搭建", + "analysisReport": "分析报告", + "moreFiles": "+{count} 个文件", + "noFiles": "暂无文件", + "loadingText": "加载中...", + "simRequirement": "模拟需求", + "relatedFiles": "关联文件", + "noRelatedFiles": "暂无关联文件", + "replayTitle": "推演回放", + "step1Button": "图谱构建", + "step2Button": "环境搭建", + "step4Button": "分析报告", + "replayHint": "Step3「开始模拟」与 Step5「深度互动」需在运行中启动,不支持历史回放" + }, + "api": { + "projectNotFound": "项目不存在: {id}", + "projectDeleteFailed": "项目不存在或删除失败: {id}", + "projectDeleted": "项目已删除: {id}", + "projectReset": "项目已重置: {id}", + "requireSimulationRequirement": "请提供模拟需求描述 (simulation_requirement)", + "requireFileUpload": "请至少上传一个文档文件", + "noDocProcessed": "没有成功处理任何文档,请检查文件格式", + "requireProjectId": "请提供 project_id", + "configError": "配置错误: {details}", + "zepApiKeyMissing": "ZEP_API_KEY未配置", + "ontologyNotGenerated": "项目尚未生成本体,请先调用 /ontology/generate", + "graphBuilding": "图谱正在构建中,请勿重复提交。如需强制重建,请添加 force: true", + "textNotFound": "未找到提取的文本内容", + "ontologyNotFound": "未找到本体定义", + "graphBuildStarted": "图谱构建任务已启动,请通过 /task/{taskId} 查询进度", + "graphBuildComplete": "图谱构建完成", + "buildFailed": "构建失败: {error}", + "taskNotFound": "任务不存在: {id}", + "graphDeleted": "图谱已删除: {id}", + "entityNotFound": "实体不存在: {id}", + "graphNotBuilt": "项目尚未构建图谱,请先调用 /api/graph/build", + "requireSimulationId": "请提供 simulation_id", + "simulationNotFound": "模拟不存在: {id}", + "projectMissingRequirement": "项目缺少模拟需求描述 (simulation_requirement)", + "prepareStarted": "准备任务已启动,请通过 /api/simulation/prepare/status 查询进度", + "alreadyPrepared": "已有完成的准备工作,无需重复生成", + "notStartedPrepare": "尚未开始准备,请调用 /api/simulation/prepare 开始", + "taskCompletedPrepared": "任务已完成(准备工作已存在)", + "requireTaskOrSimId": "请提供 task_id 或 simulation_id", + "configNotFound": "模拟配置不存在,请先调用 /prepare 接口", + "configFileNotFound": "配置文件不存在,请先调用 /prepare 接口", + "unknownScript": "未知脚本: {name},可选: {allowed}", + "scriptFileNotFound": "脚本文件不存在: {name}", + "requireGraphId": "请提供 graph_id", + "noMatchingEntities": "没有找到符合条件的实体", + "maxRoundsPositive": "max_rounds 必须是正整数", + "maxRoundsInvalid": "max_rounds 必须是有效的整数", + "invalidPlatform": "无效的平台类型: {platform},可选: twitter/reddit/parallel", + "simRunningForceHint": "模拟正在运行中,请先调用 /stop 接口停止,或使用 force=true 强制重新开始", + "simNotReady": "模拟未准备好,当前状态: {status},请先调用 /prepare 接口", + "graphIdRequiredForMemory": "启用图谱记忆更新需要有效的 graph_id,请确保项目已构建图谱", + "dbNotExist": "数据库不存在,模拟可能尚未运行", + "requireMessage": "请提供 message", + "missingGraphId": "缺少图谱ID", + "missingGraphIdEnsure": "缺少图谱ID,请确保已构建图谱", + "missingSimRequirement": "缺少模拟需求描述", + "reportAlreadyExists": "报告已存在", + "reportGenerateStarted": "报告生成任务已启动,请通过 /api/report/generate/status 查询进度", + "reportGenerated": "报告已生成", + "reportNotFound": "报告不存在: {id}", + "noReportForSim": "该模拟暂无报告: {id}", + "reportDeleted": "报告已删除: {id}", + "reportGenerateFailed": "报告生成失败", + "sectionNotFound": "章节不存在: section_{index}.md", + "reportProgressNotAvail": "报告不存在或进度信息不可用: {id}", + "requireAgentId": "请提供 agent_id", + "requirePrompt": "请提供 prompt(采访问题)", + "invalidInterviewPlatform": "platform 参数只能是 'twitter' 或 'reddit'", + "envNotRunning": "模拟环境未运行或已关闭。请确保模拟已完成并进入等待命令模式。", + "interviewTimeout": "等待Interview响应超时: {error}", + "requireInterviews": "请提供 interviews(采访列表)", + "interviewListMissingAgentId": "采访列表第{index}项缺少 agent_id", + "interviewListMissingPrompt": "采访列表第{index}项缺少 prompt", + "interviewListInvalidPlatform": "采访列表第{index}项的platform只能是 'twitter' 或 'reddit'", + "batchInterviewTimeout": "等待批量Interview响应超时: {error}", + "globalInterviewTimeout": "等待全局Interview响应超时: {error}", + "envRunning": "环境正在运行,可以接收Interview命令", + "initReportAgent": "初始化Report Agent..." + } +} From 2ffadd303840d44ed61573117382d315c8f556b7 Mon Sep 17 00:00:00 2001 From: ghostubborn Date: Wed, 1 Apr 2026 15:22:07 +0800 Subject: [PATCH 02/33] feat(i18n): add Accept-Language header to all API requests --- frontend/src/api/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/api/index.js b/frontend/src/api/index.js index e2d9465b2..e840e1166 100644 --- a/frontend/src/api/index.js +++ b/frontend/src/api/index.js @@ -1,4 +1,5 @@ import axios from 'axios' +import i18n from '../i18n' // 创建axios实例 const service = axios.create({ @@ -12,6 +13,7 @@ const service = axios.create({ // 请求拦截器 service.interceptors.request.use( config => { + config.headers['Accept-Language'] = i18n.global.locale.value return config }, error => { From 0c18e1aeca67aee3c18c8714ab37b40e3c33f9aa Mon Sep 17 00:00:00 2001 From: ghostubborn Date: Wed, 1 Apr 2026 15:22:14 +0800 Subject: [PATCH 03/33] feat(i18n): add backend translation utility with shared locale files --- backend/app/utils/__init__.py | 3 +- backend/app/utils/locale.py | 60 +++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 backend/app/utils/locale.py diff --git a/backend/app/utils/__init__.py b/backend/app/utils/__init__.py index 5848792b8..e022abb64 100644 --- a/backend/app/utils/__init__.py +++ b/backend/app/utils/__init__.py @@ -4,6 +4,7 @@ from .file_parser import FileParser from .llm_client import LLMClient +from .locale import t, get_locale, get_language_instruction -__all__ = ['FileParser', 'LLMClient'] +__all__ = ['FileParser', 'LLMClient', 't', 'get_locale', 'get_language_instruction'] diff --git a/backend/app/utils/locale.py b/backend/app/utils/locale.py new file mode 100644 index 000000000..8da76c77e --- /dev/null +++ b/backend/app/utils/locale.py @@ -0,0 +1,60 @@ +import json +import os +from flask import request, has_request_context + +_locales_dir = os.path.join(os.path.dirname(__file__), '..', '..', '..', 'locales') + +# Load language registry +with open(os.path.join(_locales_dir, 'languages.json'), 'r', encoding='utf-8') as f: + _languages = json.load(f) + +# Load translation files +_translations = {} +for filename in os.listdir(_locales_dir): + if filename.endswith('.json') and filename != 'languages.json': + locale_name = filename[:-5] + with open(os.path.join(_locales_dir, filename), 'r', encoding='utf-8') as f: + _translations[locale_name] = json.load(f) + + +def get_locale() -> str: + if has_request_context(): + return request.headers.get('Accept-Language', 'zh') + return 'zh' + + +def t(key: str, **kwargs) -> str: + locale = get_locale() + messages = _translations.get(locale, _translations.get('zh', {})) + + value = messages + for part in key.split('.'): + if isinstance(value, dict): + value = value.get(part) + else: + value = None + break + + if value is None: + value = _translations.get('zh', {}) + for part in key.split('.'): + if isinstance(value, dict): + value = value.get(part) + else: + value = None + break + + if value is None: + return key + + if kwargs: + for k, v in kwargs.items(): + value = value.replace(f'{{{k}}}', str(v)) + + return value + + +def get_language_instruction() -> str: + locale = get_locale() + lang_config = _languages.get(locale, _languages.get('zh', {})) + return lang_config.get('llmInstruction', '请使用中文回答。') From 22bf50f87798864cf53a481bc167aff2b8bd3a60 Mon Sep 17 00:00:00 2001 From: ghostubborn Date: Wed, 1 Apr 2026 15:22:14 +0800 Subject: [PATCH 04/33] feat(i18n): set up vue-i18n with dynamic locale loading --- frontend/index.html | 2 +- frontend/package-lock.json | 66 ++++++++++++++++++++++++++++++++++++++ frontend/package.json | 1 + frontend/src/i18n/index.js | 27 ++++++++++++++++ frontend/src/main.js | 2 ++ frontend/vite.config.js | 6 ++++ 6 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 frontend/src/i18n/index.js diff --git a/frontend/index.html b/frontend/index.html index 009c924a4..f6070997b 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,5 +1,5 @@ - + diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 8c4fa710d..3edf33957 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11,6 +11,7 @@ "axios": "^1.13.2", "d3": "^7.9.0", "vue": "^3.5.24", + "vue-i18n": "^9.14.5", "vue-router": "^4.6.3" }, "devDependencies": { @@ -506,6 +507,50 @@ "node": ">=18" } }, + "node_modules/@intlify/core-base": { + "version": "9.14.5", + "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.5.tgz", + "integrity": "sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==", + "license": "MIT", + "dependencies": { + "@intlify/message-compiler": "9.14.5", + "@intlify/shared": "9.14.5" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/message-compiler": { + "version": "9.14.5", + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.5.tgz", + "integrity": "sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==", + "license": "MIT", + "dependencies": { + "@intlify/shared": "9.14.5", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/shared": { + "version": "9.14.5", + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.5.tgz", + "integrity": "sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", @@ -2035,6 +2080,27 @@ } } }, + "node_modules/vue-i18n": { + "version": "9.14.5", + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.5.tgz", + "integrity": "sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==", + "deprecated": "v9 and v10 no longer supported. please migrate to v11. about maintenance status, see https://vue-i18n.intlify.dev/guide/maintenance.html", + "license": "MIT", + "dependencies": { + "@intlify/core-base": "9.14.5", + "@intlify/shared": "9.14.5", + "@vue/devtools-api": "^6.5.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, "node_modules/vue-router": { "version": "4.6.3", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.3.tgz", diff --git a/frontend/package.json b/frontend/package.json index f7e995a14..7e05e8918 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,6 +12,7 @@ "axios": "^1.13.2", "d3": "^7.9.0", "vue": "^3.5.24", + "vue-i18n": "^9.14.5", "vue-router": "^4.6.3" }, "devDependencies": { diff --git a/frontend/src/i18n/index.js b/frontend/src/i18n/index.js new file mode 100644 index 000000000..aa2655352 --- /dev/null +++ b/frontend/src/i18n/index.js @@ -0,0 +1,27 @@ +import { createI18n } from 'vue-i18n' +import languages from '../../../locales/languages.json' + +const localeFiles = import.meta.glob('../../../locales/!(languages).json', { eager: true }) + +const messages = {} +const availableLocales = [] + +for (const path in localeFiles) { + const key = path.match(/\/([^/]+)\.json$/)[1] + if (languages[key]) { + messages[key] = localeFiles[path].default + availableLocales.push({ key, label: languages[key].label }) + } +} + +const savedLocale = localStorage.getItem('locale') || 'zh' + +const i18n = createI18n({ + legacy: false, + locale: savedLocale, + fallbackLocale: 'zh', + messages +}) + +export { availableLocales } +export default i18n diff --git a/frontend/src/main.js b/frontend/src/main.js index c8e37b03b..cc3d101e4 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -1,9 +1,11 @@ import { createApp } from 'vue' import App from './App.vue' import router from './router' +import i18n from './i18n' const app = createApp(App) app.use(router) +app.use(i18n) app.mount('#app') diff --git a/frontend/vite.config.js b/frontend/vite.config.js index 7cec1a712..f88fdea8b 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -1,9 +1,15 @@ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' +import path from 'path' // https://vite.dev/config/ export default defineConfig({ plugins: [vue()], + resolve: { + alias: { + '@locales': path.resolve(__dirname, '../locales') + } + }, server: { port: 3000, open: true, From 8f6110df0fa08b9091dfb8434800141a09a9b37a Mon Sep 17 00:00:00 2001 From: ghostubborn Date: Wed, 1 Apr 2026 15:24:12 +0800 Subject: [PATCH 05/33] feat(i18n): inject language instruction into LLM system prompts --- backend/app/services/ontology_generator.py | 4 +++- backend/app/services/report_agent.py | 5 ++++- backend/app/services/simulation_config_generator.py | 10 +++++++--- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/backend/app/services/ontology_generator.py b/backend/app/services/ontology_generator.py index 2d3e39bd8..2d0b23049 100644 --- a/backend/app/services/ontology_generator.py +++ b/backend/app/services/ontology_generator.py @@ -6,6 +6,7 @@ import json from typing import Dict, Any, List, Optional from ..utils.llm_client import LLMClient +from ..utils.locale import get_language_instruction # 本体生成的系统提示词 @@ -188,8 +189,9 @@ def generate( additional_context ) + system_prompt = f"{ONTOLOGY_SYSTEM_PROMPT}\n\n{get_language_instruction()}" messages = [ - {"role": "system", "content": ONTOLOGY_SYSTEM_PROMPT}, + {"role": "system", "content": system_prompt}, {"role": "user", "content": user_message} ] diff --git a/backend/app/services/report_agent.py b/backend/app/services/report_agent.py index 02ca5bdc2..7a4c8b348 100644 --- a/backend/app/services/report_agent.py +++ b/backend/app/services/report_agent.py @@ -21,6 +21,7 @@ from ..config import Config from ..utils.llm_client import LLMClient from ..utils.logger import get_logger +from ..utils.locale import get_language_instruction from .zep_tools import ( ZepToolsService, SearchResult, @@ -1162,7 +1163,7 @@ def plan_outline( if progress_callback: progress_callback("planning", 30, "正在生成报告大纲...") - system_prompt = PLAN_SYSTEM_PROMPT + system_prompt = f"{PLAN_SYSTEM_PROMPT}\n\n{get_language_instruction()}" user_prompt = PLAN_USER_PROMPT_TEMPLATE.format( simulation_requirement=self.simulation_requirement, total_nodes=context.get('graph_statistics', {}).get('total_nodes', 0), @@ -1258,6 +1259,7 @@ def _generate_section_react( section_title=section.title, tools_description=self._get_tools_description(), ) + system_prompt = f"{system_prompt}\n\n{get_language_instruction()}" # 构建用户prompt - 每个已完成章节各传入最大4000字 if previous_sections: @@ -1805,6 +1807,7 @@ def chat( report_content=report_content if report_content else "(暂无报告)", tools_description=self._get_tools_description(), ) + system_prompt = f"{system_prompt}\n\n{get_language_instruction()}" # 构建消息 messages = [{"role": "system", "content": system_prompt}] diff --git a/backend/app/services/simulation_config_generator.py b/backend/app/services/simulation_config_generator.py index cc362508b..328369de0 100644 --- a/backend/app/services/simulation_config_generator.py +++ b/backend/app/services/simulation_config_generator.py @@ -20,6 +20,7 @@ from ..config import Config from ..utils.logger import get_logger +from ..utils.locale import get_language_instruction from .zep_entity_reader import EntityNode, ZepEntityReader logger = get_logger('mirofish.simulation_config') @@ -585,7 +586,8 @@ def _generate_time_config(self, context: str, num_entities: int) -> Dict[str, An - reasoning (string): 简要说明为什么这样配置""" system_prompt = "你是社交媒体模拟专家。返回纯JSON格式,时间配置需符合中国人作息习惯。" - + system_prompt = f"{system_prompt}\n\n{get_language_instruction()}" + try: return self._call_llm_with_retry(prompt, system_prompt) except Exception as e: @@ -701,7 +703,8 @@ def _generate_event_config( }}""" system_prompt = "你是舆论分析专家。返回纯JSON格式。注意 poster_type 必须精确匹配可用实体类型。" - + system_prompt = f"{system_prompt}\n\n{get_language_instruction()}" + try: return self._call_llm_with_retry(prompt, system_prompt) except Exception as e: @@ -864,7 +867,8 @@ def _generate_agent_configs_batch( }}""" system_prompt = "你是社交媒体行为分析专家。返回纯JSON,配置需符合中国人作息习惯。" - + system_prompt = f"{system_prompt}\n\n{get_language_instruction()}" + try: result = self._call_llm_with_retry(prompt, system_prompt) llm_configs = {cfg["agent_id"]: cfg for cfg in result.get("agent_configs", [])} From 3d5e5d024d113fad718515372004339cde0894b3 Mon Sep 17 00:00:00 2001 From: ghostubborn Date: Wed, 1 Apr 2026 15:24:58 +0800 Subject: [PATCH 06/33] feat(i18n): add language switcher component to navigation --- frontend/src/components/LanguageSwitcher.vue | 153 +++++++++++++++++++ frontend/src/views/Home.vue | 3 + frontend/src/views/InteractionView.vue | 3 + frontend/src/views/MainView.vue | 3 + frontend/src/views/ReportView.vue | 3 + frontend/src/views/SimulationRunView.vue | 3 + frontend/src/views/SimulationView.vue | 3 + 7 files changed, 171 insertions(+) create mode 100644 frontend/src/components/LanguageSwitcher.vue diff --git a/frontend/src/components/LanguageSwitcher.vue b/frontend/src/components/LanguageSwitcher.vue new file mode 100644 index 000000000..99b29df72 --- /dev/null +++ b/frontend/src/components/LanguageSwitcher.vue @@ -0,0 +1,153 @@ + + + + + diff --git a/frontend/src/views/Home.vue b/frontend/src/views/Home.vue index afe01a0c4..c8fb49ec0 100644 --- a/frontend/src/views/Home.vue +++ b/frontend/src/views/Home.vue @@ -4,6 +4,7 @@ @@ -16,21 +16,25 @@
- 简洁通用的群体智能引擎 - / v0.1-预览版 + {{ $t('home.tagline') }} + {{ $t('home.version') }}

- 上传任意报告
- 即刻推演未来 + {{ $t('home.heroTitle1') }}
+ {{ $t('home.heroTitle2') }}

-
+

- 即使只有一段文字,MiroFish 也能基于其中的现实种子,全自动生成与之对应的至多百万级Agent构成的平行世界。通过上帝视角注入变量,在复杂的群体交互中寻找动态环境下的“局部最优解” + + + + +

-

- 让未来在 Agent 群中预演,让决策在百战后胜出_ +

+ {{ $t('home.slogan') }}_

@@ -54,65 +58,65 @@
- 系统状态 + {{ $t('home.systemStatus') }}
-

准备就绪

+

{{ $t('home.systemReady') }}

- 预测引擎待命中,可上传多份非结构化数据以初始化模拟序列 + {{ $t('home.systemReadyDesc') }}

-
低成本
-
常规模拟平均5$/次
+
{{ $t('home.metricLowCost') }}
+
{{ $t('home.metricLowCostDesc') }}
-
高可用
-
最多百万级Agent模拟
+
{{ $t('home.metricHighAvail') }}
+
{{ $t('home.metricHighAvailDesc') }}
- 工作流序列 + {{ $t('home.workflowSequence') }}
01
-
图谱构建
-
现实种子提取 & 个体与群体记忆注入 & GraphRAG构建
+
{{ $t('home.step01Title') }}
+
{{ $t('home.step01Desc') }}
02
-
环境搭建
-
实体关系抽取 & 人设生成 & 环境配置Agent注入仿真参数
+
{{ $t('home.step02Title') }}
+
{{ $t('home.step02Desc') }}
03
-
开始模拟
-
双平台并行模拟 & 自动解析预测需求 & 动态更新时序记忆
+
{{ $t('home.step03Title') }}
+
{{ $t('home.step03Desc') }}
04
-
报告生成
-
ReportAgent拥有丰富的工具集与模拟后环境进行深度交互
+
{{ $t('home.step04Title') }}
+
{{ $t('home.step04Desc') }}
05
-
深度互动
-
与模拟世界中的任意一位进行对话 & 与ReportAgent进行对话
+
{{ $t('home.step05Title') }}
+
{{ $t('home.step05Desc') }}
@@ -125,8 +129,8 @@
- 01 / 现实种子 - 支持格式: PDF, MD, TXT + {{ $t('home.realitySeed') }} + {{ $t('home.supportedFormats') }}
-
拖拽文件上传
-
或点击浏览文件系统
+
{{ $t('home.dragToUpload') }}
+
{{ $t('home.orBrowse') }}
@@ -165,23 +169,23 @@
- 输入参数 + {{ $t('home.inputParams') }}
- >_ 02 / 模拟提示词 + {{ $t('home.simulationPrompt') }}
-
引擎: MiroFish-V1.0
+
{{ $t('home.engineBadge') }}
@@ -192,8 +196,8 @@ @click="startSimulation" :disabled="!canSubmit || loading" > - 启动引擎 - 初始化中... + {{ $t('home.startEngine') }} + {{ $t('home.initializing') }}
diff --git a/frontend/src/views/InteractionView.vue b/frontend/src/views/InteractionView.vue index 05268ba9e..a2de0f4ec 100644 --- a/frontend/src/views/InteractionView.vue +++ b/frontend/src/views/InteractionView.vue @@ -15,7 +15,7 @@ :class="{ active: viewMode === mode }" @click="viewMode = mode" > - {{ { graph: '图谱', split: '双栏', workbench: '工作台' }[mode] }} + {{ { graph: $t('main.layoutGraph'), split: $t('main.layoutSplit'), workbench: $t('main.layoutWorkbench') }[mode] }}
@@ -25,7 +25,7 @@
Step 5/5 - 深度互动 + {{ $tm('main.stepNames')[4] }}
diff --git a/frontend/src/views/MainView.vue b/frontend/src/views/MainView.vue index 37e36898e..41a0a3745 100644 --- a/frontend/src/views/MainView.vue +++ b/frontend/src/views/MainView.vue @@ -15,7 +15,7 @@ :class="{ active: viewMode === mode }" @click="viewMode = mode" > - {{ { graph: '图谱', split: '双栏', workbench: '工作台' }[mode] }} + {{ { graph: $t('main.layoutGraph'), split: $t('main.layoutSplit'), workbench: $t('main.layoutWorkbench') }[mode] }}
@@ -25,7 +25,7 @@
Step {{ currentStep }}/5 - {{ stepNames[currentStep - 1] }} + {{ $tm('main.stepNames')[currentStep - 1] }}
@@ -79,6 +79,7 @@ diff --git a/frontend/src/views/MainView.vue b/frontend/src/views/MainView.vue index 41a0a3745..513c70d83 100644 --- a/frontend/src/views/MainView.vue +++ b/frontend/src/views/MainView.vue @@ -164,11 +164,11 @@ const toggleMaximize = (target) => { const handleNextStep = (params = {}) => { if (currentStep.value < 5) { currentStep.value++ - addLog(`进入 Step ${currentStep.value}: ${stepNames.value[currentStep.value - 1]}`) + addLog(t('log.enterStep', { step: currentStep.value, name: stepNames.value[currentStep.value - 1] })) // 如果是从 Step 2 进入 Step 3,记录模拟轮数配置 if (currentStep.value === 3 && params.maxRounds) { - addLog(`自定义模拟轮数: ${params.maxRounds} 轮`) + addLog(t('log.customSimRounds', { rounds: params.maxRounds })) } } } @@ -176,7 +176,7 @@ const handleNextStep = (params = {}) => { const handleGoBack = () => { if (currentStep.value > 1) { currentStep.value-- - addLog(`返回 Step ${currentStep.value}: ${stepNames.value[currentStep.value - 1]}`) + addLog(t('log.returnToStep', { step: currentStep.value, name: stepNames.value[currentStep.value - 1] })) } } diff --git a/frontend/src/views/ReportView.vue b/frontend/src/views/ReportView.vue index e8dbf5a66..ac054e47b 100644 --- a/frontend/src/views/ReportView.vue +++ b/frontend/src/views/ReportView.vue @@ -66,6 +66,7 @@ From 380e456d413d1b82bfd32c5aa73d1cd370899428 Mon Sep 17 00:00:00 2001 From: ghostubborn Date: Wed, 1 Apr 2026 17:31:00 +0800 Subject: [PATCH 21/33] fix(i18n): replace hardcoded Chinese stage names in simulation prepare SSE --- backend/app/api/simulation.py | 8 ++++---- locales/en.json | 6 +++++- locales/zh.json | 6 +++++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/backend/app/api/simulation.py b/backend/app/api/simulation.py index 2c1b6b1ad..3a8e1e3fc 100644 --- a/backend/app/api/simulation.py +++ b/backend/app/api/simulation.py @@ -533,10 +533,10 @@ def progress_callback(stage, progress, message, **kwargs): # 构建详细进度信息 stage_names = { - "reading": "读取图谱实体", - "generating_profiles": "生成Agent人设", - "generating_config": "生成模拟配置", - "copying_scripts": "准备模拟脚本" + "reading": t('progress.readingGraphEntities'), + "generating_profiles": t('progress.generatingProfiles'), + "generating_config": t('progress.generatingSimConfig'), + "copying_scripts": t('progress.preparingScripts') } stage_index = list(stage_weights.keys()).index(stage) + 1 if stage in stage_weights else 1 diff --git a/locales/en.json b/locales/en.json index 1d279e582..4601b92aa 100644 --- a/locales/en.json +++ b/locales/en.json @@ -409,7 +409,11 @@ "eventConfigLabel": "Event Config", "agentConfigResult": "Agent Config: {count} generated", "postAssignResult": "Post Assignment: {count} posts assigned", - "profileGenerated": "[Generated] {name} ({type})" + "profileGenerated": "[Generated] {name} ({type})", + "readingGraphEntities": "Reading Graph Entities", + "generatingProfiles": "Generating Agent Profiles", + "generatingSimConfig": "Generating Simulation Config", + "preparingScripts": "Preparing Scripts" }, "log": { "preparingGoBack": "Preparing to return to Step 2, closing simulation...", diff --git a/locales/zh.json b/locales/zh.json index 3399db30f..11e19c90a 100644 --- a/locales/zh.json +++ b/locales/zh.json @@ -409,7 +409,11 @@ "eventConfigLabel": "事件配置", "agentConfigResult": "Agent配置: 成功生成 {count} 个", "postAssignResult": "初始帖子分配: {count} 个帖子已分配发布者", - "profileGenerated": "[已生成] {name} ({type})" + "profileGenerated": "[已生成] {name} ({type})", + "readingGraphEntities": "读取图谱实体", + "generatingProfiles": "生成Agent人设", + "generatingSimConfig": "生成模拟配置", + "preparingScripts": "准备模拟脚本" }, "log": { "preparingGoBack": "准备返回 Step 2,正在关闭模拟...", From 1d358fc492c854d598cd032d9fd4f025d3a49d83 Mon Sep 17 00:00:00 2001 From: ghostubborn Date: Wed, 1 Apr 2026 17:44:45 +0800 Subject: [PATCH 22/33] feat(i18n): replace expand/collapse Chinese text in Step4Report.vue --- frontend/src/components/Step4Report.vue | 25 +++--- locales/en.json | 102 +++++++++++++++++++++++- locales/zh.json | 102 +++++++++++++++++++++++- 3 files changed, 217 insertions(+), 12 deletions(-) diff --git a/frontend/src/components/Step4Report.vue b/frontend/src/components/Step4Report.vue index 214fea686..ee7113525 100644 --- a/frontend/src/components/Step4Report.vue +++ b/frontend/src/components/Step4Report.vue @@ -392,9 +392,11 @@