在研究一个俄语界面的程序,为了把界面的俄语翻译为中文,让AI写个html 程序能够使用火山引擎API把csharp UI文件中控件的text属性从俄语翻译为中文。(提示词需要根据效果进行调整)。
这是一个C# (.cs) 文件控件文本翻译器。它的主要作用是读取用户上传的C#源代码文件,利用火山引擎大模型API,将代码中面向用户的界面文本(例如按钮文字、标签内容、消息框提示等)从俄语翻译成中文,同时保持原始代码的结构和语法不变。
功能分解
用户界面 (UI) 与交互 (UX):
- 现代化设计
:页面采用了渐变背景、圆角、阴影等现代CSS样式,提供了美观且专业的视觉体验。
- 响应式布局
:通过
@media查询,界面能够适应不同尺寸的屏幕(如桌面和手机),保证了在小屏幕设备上的可用性。 - 清晰的流程引导
:界面被划分为API配置、翻译配置、文件上传、进度显示和结果下载等几个逻辑区域,引导用户按步骤完成操作。
配置区域:
- API配置
:用户需要输入自己的火山引擎API信息,包括:
API Key (Bearer Token):用于身份验证,输入框为密码类型以保护密钥。
模型名称:允许用户自定义调用的AI模型,并提供了一个默认值。
API端点:指定API请求的URL,同样提供了默认值。
- 翻译配置
:用户可以精细化控制翻译过程:
自定义提示词 (Prompt):提供了一个强大的文本框,用户可以编辑给AI的指令。默认提示词非常专业,明确要求AI扮演C#本地化专家,只翻译俄语字符串,并保持代码结构、格式不变,还给出了翻译示例。
批次大小 (Batch Size):允许用户选择每次发送给API的文本块大小(以字符为单位)。这是为了处理大文件,避免超出API单次请求的长度限制。
输出文件名:用户可以指定翻译后下载的文件的名称。
- API配置
文件处理:
- 文件上传
:提供一个样式精美的按钮来选择本地的
.cs文件。 - 文件信息显示
:选择文件后,页面会立即显示该文件的名称、大小和最后修改日期,提供即时反馈。
- 智能命名
:如果用户没有指定输出文件名,程序会自动根据上传的文件名生成一个(例如
MyForm.cs会变成MyForm_translated.cs)。 - 客户端读取
:文件内容完全在用户的浏览器中通过
FileReaderAPI进行读取,不会上传到任何中间服务器,保证了代码的私密性。
- 文件上传
核心翻译逻辑 (JavaScript):
- 分批处理 (Batching)
:
startTranslation函数会将整个C#文件内容根据设定的“批次大小”切割成多个小文本块。 - 异步API调用
:程序会循环遍历每一个文本块,使用
async/await和fetch函数,逐个调用火山引擎API进行翻译。 - 实时进度反馈
:在翻译过程中:
一个进度条会实时更新,显示总体完成百分比。
旁边的文本会提示当前正在处理哪个批次(如 “正在翻译第 2/10 个批次...”)。
下方的**日志控制台 (Log Container)**会实时打印详细的操作日志,包括文件读取、分批情况、每个批次的翻译成功或失败信息,并用不同颜色(信息、成功、错误)加以区分,便于追踪和调试。
- 健壮的错误处理
:
API调用部分有详细的错误捕获机制,如果API返回错误,会在日志中明确提示失败原因。
即使某个批次翻译失败,程序也会将该批次的原文保留,然后继续处理下一个批次,确保最终能生成一个完整的文件,而不是中途中断。
- 结果整合与下载
:所有批次处理完毕后,程序会将翻译后的(或翻译失败保留的原文)文本块重新组合成一个完整的代码文件内容。然后显示“下载”按钮,用户点击后即可将结果保存到本地。
总结
总而言之,这是一个纯前端、功能强大且用户体验良好的开发者工具。它通过调用外部AI服务,解决了C#代码本地化过程中繁琐的文本翻译工作,实现了以下几个关键点:
- 自动化
:将手动翻译代码中字符串的工作自动化。
- 智能化
:利用大模型的能力进行高质量、符合上下文的翻译。
- 安全性
:代码文件仅在用户本地浏览器处理,不经过第三方服务器。
- 高可用性
:通过分批处理和错误处理机制,保证了对大文件处理的稳定性和可靠性。
- 用户友好
:界面直观,提供实时反馈,让复杂的过程变得透明可控。

<!DOCTYPE html><html lang="zh-CN"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>CS文件控件文本翻译器</title> <!-- 修复:移除了CryptoJS库,因为Bearer认证不需要它 --> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; padding: 20px; } .container { max-width: 1200px; margin: 0 auto; background: white; border-radius: 20px; box-shadow: 0 20px 40px rgba(0,0,0,0.1); overflow: hidden; } .header { background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); color: white; padding: 30px; text-align: center; } .header h1 { font-size: 2.5em; margin-bottom: 10px; text-shadow: 0 2px 4px rgba(0,0,0,0.3); } .header p { font-size: 1.1em; opacity: 0.9; } .main-content { padding: 40px; } .config-section { background: #f8f9ff; padding: 25px; border-radius: 15px; margin-bottom: 30px; border-left: 5px solid #4facfe; } .config-section h3 { color: #333; margin-bottom: 20px; font-size: 1.3em; } .form-group { margin-bottom: 20px; } .form-group label { display: block; margin-bottom: 8px; font-weight: 600; color: #555; } .form-group input, .form-group textarea, .form-group select { width: 100%; padding: 12px; border: 2px solid #e1e5e9; border-radius: 8px; font-size: 14px; transition: border-color 0.3s ease; } .form-group input:focus, .form-group textarea:focus, .form-group select:focus { outline: none; border-color: #4facfe; box-shadow: 0 0 0 3px rgba(79, 172, 254, 0.1); } .form-group textarea { resize: vertical; min-height: 100px; } .form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; } .file-upload { position: relative; display: inline-block; width: 100%; } .file-upload input[type=file] { position: absolute; left: -9999px; } .file-upload-label { display: block; padding: 15px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; text-align: center; border-radius: 8px; cursor: pointer; transition: transform 0.3s ease; } .file-upload-label:hover { transform: translateY(-2px); } .btn { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 15px 30px; border: none; border-radius: 8px; font-size: 16px; font-weight: 600; cursor: pointer; transition: all 0.3s ease; text-transform: uppercase; letter-spacing: 1px; } .btn:hover { transform: translateY(-2px); box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3); } .btn:disabled { opacity: 0.6; cursor: not-allowed; transform: none; } .progress-container { margin-top: 20px; padding: 20px; background: #f8f9ff; border-radius: 10px; border-left: 5px solid #4facfe; } .progress-bar { width: 100%; height: 20px; background: #e1e5e9; border-radius: 10px; overflow: hidden; margin-bottom: 15px; } .progress-fill { height: 100%; background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); width: 0%; transition: width 0.3s ease; } .log-container { background: #1e1e1e; color: #00ff00; padding: 20px; border-radius: 10px; max-height: 300px; overflow-y: auto; font-family: 'Courier New', monospace; font-size: 14px; line-height: 1.4; } .log-entry { margin-bottom: 5px; } .log-entry.error { color: #ff6b6b; } .log-entry.success { color: #51cf66; } .log-entry.info { color: #74c0fc; } .result-section { margin-top: 30px; padding: 25px; background: #f8f9ff; border-radius: 15px; border-left: 5px solid #51cf66; } .download-btn { background: linear-gradient(135deg, #51cf66 0%, #40c057 100%); margin-top: 15px; } .download-btn:hover { box-shadow: 0 10px 20px rgba(81, 207, 102, 0.3); } @media (max-width: 768px) { .form-row { grid-template-columns: 1fr; } .header h1 { font-size: 2em; } .main-content { padding: 20px; } } </style></head><body> <div class="container"> <div class="header"> <h1>🔧 CS文件控件文本翻译器</h1> <p>使用火山引擎API翻译C#文件中的控件显示文本</p> </div> <div class="main-content"> <!-- API配置区域 --> <div class="config-section"> <h3>🔑 API配置 (API Configuration)</h3> <!-- 修复:恢复为单个API Key输入框,移除Secret Key --> <div class="form-group"> <label for="apiKey">API Key (Bearer Token):</label> <input type="password" id="apiKey" placeholder="请输入火山方舟API Key"> </div> <!-- 新增:模型名称输入框,使其更灵活 --> <div class="form-group"> <label for="modelName">模型名称 (Model Name):</label> <input type="text" id="modelName" value="deepseek-v3-1-250821"> </div> <div class="form-group"> <label for="endpoint">API端点 (Endpoint):</label> <!-- 修复:移除value开头的空格 --> <input type="url" id="endpoint" placeholder="https://ark.cn-beijing.volces.com/api/v3/chat/completions" value="https://ark.cn-beijing.volces.com/api/v3/chat/completions"> </div> </div> <!-- 翻译配置区域 --> <div class="config-section"> <h3>⚙️ 翻译配置 (Translation Settings)</h3> <div class="form-group"> <label for="customPrompt">自定义提示词 (Custom Prompt):</label> <textarea id="customPrompt" placeholder="请输入自定义提示词,用于指导AI如何翻译控件文本">你是一个专业的C#界面本地化翻译专家。请将以下C#代码中的控件俄语文本(如按钮文字、标签文本、消息框内容等)翻译为中文,保持代码结构不变,只翻译用户界面显示的文本内容。 翻译要求:1. 只翻译字符串字面量中的俄语文本内容2. 保持所有代码语法、结构、变量名、方法名完全不变3. 保持缩进和格式不变4. 专业术语要准确,符合中文软件界面习惯5. 不要添加额外的注释或说明 示例:原文:this.btnHello.Text = "Привет";译文:this.btnHello.Text = "你好";;</textarea> </div> <div class="form-row"> <div class="form-group"> <label for="batchSize">批次大小 (Batch Size):</label> <select id="batchSize"> <option value="1000">1000字符</option> <option value="2000" selected>2000字符</option> <option value="3000">3000字符</option> <option value="5000">5000字符</option> </select> </div> <div class="form-group"> <label for="outputFileName">输出文件名 (Output Filename):</label> <input type="text" id="outputFileName" placeholder="translated_file.cs" value=""> </div> </div> </div> <!-- 文件上传区域 --> <div class="config-section"> <h3>📁 文件上传 (File Upload)</h3> <div class="form-group"> <div class="file-upload"> <input type="file" id="csFile" accept=".cs" /> <label for="csFile" class="file-upload-label"> 📎 选择CS文件 (Select CS File) </label> </div> <div id="fileInfo" style="margin-top: 10px; color: #666;"></div> </div> </div> <!-- 操作按钮 --> <div style="text-align: center; margin: 30px 0;"> <button class="btn" id="translateBtn" onclick="startTranslation()"> 🚀 开始翻译 (Start Translation) </button> </div> <!-- 进度显示区域 --> <div id="progressContainer" class="progress-container" style="display: none;"> <h3>📊 翻译进度 (Translation Progress)</h3> <div class="progress-bar"> <div class="progress-fill" id="progressFill"></div> </div> <div id="progressText">准备中... (Preparing...)</div> <div class="log-container" id="logContainer"> <div class="log-entry info">系统初始化完成 (System initialized)</div> </div> </div> <!-- 结果区域 --> <div id="resultSection" class="result-section" style="display: none;"> <h3>✅ 翻译完成 (Translation Complete)</h3> <p>翻译已完成,点击下载按钮获取翻译后的文件。</p> <button class="btn download-btn" id="downloadBtn" onclick="downloadResult()"> 💾 下载翻译文件 (Download Translated File) </button> </div> </div> </div> <script> let translatedContent = ''; let currentFile = null; document.getElementById('csFile').addEventListener('change', function(e) { const file = e.target.files[0]; const fileInfo = document.getElementById('fileInfo'); if (file) { currentFile = file; fileInfo.innerHTML = ` <strong>已选择文件:</strong> ${file.name}<br> <strong>文件大小:</strong> ${(file.size / 1024).toFixed(2)} KB<br> <strong>最后修改:</strong> ${new Date(file.lastModified).toLocaleString()} `; const outputFileName = document.getElementById('outputFileName'); if (!outputFileName.value) { const baseName = file.name.replace('.cs', ''); outputFileName.value = `${baseName}_translated.cs`; } } else { currentFile = null; fileInfo.innerHTML = ''; } }); function addLog(message, type = 'info') { const logContainer = document.getElementById('logContainer'); const logEntry = document.createElement('div'); logEntry.className = `log-entry ${type}`; logEntry.innerHTML = `[${new Date().toLocaleTimeString()}] ${message}`; logContainer.appendChild(logEntry); logContainer.scrollTop = logContainer.scrollHeight; } function updateProgress(percent, text) { document.getElementById('progressFill').style.width = `${percent}%`; document.getElementById('progressText').textContent = text; } // 修复:根据curl示例重写API调用函数 async function callVolcanoAPI(text, prompt) { const apiKey = document.getElementById('apiKey').value; const endpoint = document.getElementById('endpoint').value.trim(); const modelName = document.getElementById('modelName').value.trim(); if (!apiKey) { throw new Error('请输入API Key'); } if (!endpoint) { throw new Error('请输入API端点URL'); } if (!modelName) { throw new Error('请输入模型名称'); } // 1. 构造请求体 (Body) - 完全匹配curl示例 const requestBody = { model: modelName, messages: [ { role: 'system', content: prompt }, { role: 'user', content: text } ] }; // 2. 构造请求头 (Headers) - 完全匹配curl示例 const headers = { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` }; // 3. 发送API请求 const response = await fetch(endpoint, { method: 'POST', headers: headers, body: JSON.stringify(requestBody) }); // 4. 处理响应 if (!response.ok) { const errorText = await response.text(); let errorData; try { errorData = JSON.parse(errorText); } catch(e) { errorData = { message: errorText || '未知错误' }; } throw new Error(`API调用失败: ${response.status} - ${errorData.message || errorData.error?.message || '无法解析错误信息'}`); } const data = await response.json(); if (data.error) { throw new Error(`API返回错误: ${data.error.message}`); } // 提取API返回的文本 return data.choices?.[0]?.message?.content || text; } function splitTextIntoBatches(text, batchSize) { const batches = []; let currentBatch = ''; const lines = text.split('\n'); for (const line of lines) { if (currentBatch.length + line.length + 1 > batchSize && currentBatch.length > 0) { batches.push(currentBatch); currentBatch = line + '\n'; } else { currentBatch += line + '\n'; } } if (currentBatch.trim()) { batches.push(currentBatch); } return batches; } async function startTranslation() { if (!currentFile) { alert('请先选择一个CS文件'); return; } const apiKey = document.getElementById('apiKey').value; if (!apiKey) { alert('请输入API Key'); return; } const translateBtn = document.getElementById('translateBtn'); const progressContainer = document.getElementById('progressContainer'); const resultSection = document.getElementById('resultSection'); translateBtn.disabled = true; progressContainer.style.display = 'block'; resultSection.style.display = 'none'; translatedContent = ''; document.getElementById('logContainer').innerHTML = ''; addLog('系统初始化完成 (System initialized)', 'info'); try { addLog('开始读取文件... (Reading file...)', 'info'); const fileContent = await new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = e => resolve(e.target.result); reader.onerror = reject; reader.readAsText(currentFile, 'utf-8'); }); addLog(`文件读取完成,大小: ${fileContent.length} 字符`, 'success'); const batchSize = parseInt(document.getElementById('batchSize').value); const customPrompt = document.getElementById('customPrompt').value; const batches = splitTextIntoBatches(fileContent, batchSize); addLog(`文件已分割为 ${batches.length} 个批次`, 'info'); let translatedBatches = []; for (let i = 0; i < batches.length; i++) { const batch = batches[i]; const progress = ((i + 1) / batches.length) * 100; updateProgress(progress, `正在翻译第 ${i + 1}/${batches.length} 个批次...`); addLog(`开始翻译批次 ${i + 1}/${batches.length}`, 'info'); try { // 启用真实API调用 const translatedBatch = await callVolcanoAPI(batch, customPrompt); translatedBatches.push(translatedBatch); addLog(`批次 ${i + 1} 翻译完成`, 'success'); } catch (error) { addLog(`批次 ${i + 1} 翻译失败: ${error.message}`, 'error'); translatedBatches.push(batch); } } translatedContent = translatedBatches.join(''); updateProgress(100, '翻译完成!'); addLog('所有批次翻译完成,准备下载...', 'success'); resultSection.style.display = 'block'; } catch (error) { addLog(`翻译过程中发生错误: ${error.message}`, 'error'); alert(`翻译失败: ${error.message}`); } finally { translateBtn.disabled = false; } } function downloadResult() { if (!translatedContent) { alert('没有可下载的内容'); return; } const outputFileName = document.getElementById('outputFileName').value || 'translated_file.cs'; const blob = new Blob([translatedContent], { type: 'text/plain;charset=utf-8' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = outputFileName; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); addLog(`文件已下载: ${outputFileName}`, 'success'); } </script></body></html>- 分批处理 (Batching)

被折叠的 条评论
为什么被折叠?



