Rouyan:使用WPF/C#构建的基于LLM的快捷翻译小工具

Rouyan:使用WPF/C#构建的基于LLM的快捷翻译小工具 Rouyan使用WPF/C#构建的基于LLM的快捷翻译小工具引言在全球化日益加深的今天翻译工具已成为开发者、写作者和普通用户的刚需。虽然市面上有谷歌翻译、DeepL等成熟产品但有时我们需要一个轻量、可定制、甚至能结合大语言模型LLM的本地翻译工具。为此我使用WPF和C#构建了一个名为“Rouyan”的快捷翻译小工具。本文将带你从零开始深入Rouyan的架构、实现细节并展示完整的代码示例。## 项目背景与目标Rouyan的设计初衷是 - 提供一个系统托盘常驻的翻译工具支持快捷键唤醒。 - 通过LLM如OpenAI API或本地模型提供高精度翻译尤其是针对专业术语和上下文敏感内容。 - 保持轻量级所有配置存储在本地JSON文件。 - 支持中英互译并预留扩展接口。## 技术栈概览-UI框架WPFWindows Presentation Foundation因为其原生支持XAML和系统托盘集成。 -语言C#用于业务逻辑和API调用。 -LLM接口基于HTTP的REST API如OpenAI的Chat Completion使用HttpClient。 -配置管理Microsoft.Extensions.Configuration结合JSON文件。 -快捷键使用GlobalHotKey库或原生Windows API。## 核心架构模块化设计Rouyan分为三个核心模块 1.UI层系统托盘图标、浮动翻译窗口、设置界面。 2.翻译引擎封装LLM API调用支持异步和缓存。 3.配置管理读取/写入用户偏好如API密钥、目标语言、快捷键。## 实战代码示例1系统托盘与快捷键绑定以下代码展示了如何创建系统托盘图标并绑定全局快捷键CtrlShiftT来弹出翻译窗口。这是Rouyan的“门面”部分。csharpusing System;using System.Windows;using System.Windows.Forms;using System.Runtime.InteropServices;namespace Rouyan{ public partial class App : Application { private NotifyIcon _trayIcon; private const int MOD_CONTROL 0x0002; private const int MOD_SHIFT 0x0004; private const int WM_HOTKEY 0x0312; private const int HOTKEY_ID 9000; // 注册全局热键的Win32 API [DllImport(user32.dll)] private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk); [DllImport(user32.dll)] private static extern bool UnregisterHotKey(IntPtr hWnd, int id); protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); // 创建系统托盘图标 _trayIcon new NotifyIcon { Icon System.Drawing.Icon.ExtractAssociatedIcon(icon.ico), // 替换为你的图标文件 Text Rouyan 翻译工具, Visible true }; _trayIcon.Click (s, args) ShowTranslationWindow(); // 注册全局热键CtrlShiftT IntPtr handle new System.Windows.Interop.WindowInteropHelper(this.MainWindow).Handle; RegisterHotKey(handle, HOTKEY_ID, MOD_CONTROL | MOD_SHIFT, (uint)Keys.T); // 监听热键消息 ComponentDispatcher.ThreadPreprocessMessage (ref MSG msg, ref bool handled) { if (msg.message WM_HOTKEY msg.wParam.ToInt32() HOTKEY_ID) { ShowTranslationWindow(); handled true; } }; } private void ShowTranslationWindow() { // 创建或激活翻译窗口这里简化实际应检查是否已存在 var window new TranslationWindow(); window.Show(); window.Activate(); } protected override void OnExit(ExitEventArgs e) { // 注销热键并清理托盘图标 IntPtr handle new System.Windows.Interop.WindowInteropHelper(this.MainWindow).Handle; UnregisterHotKey(handle, HOTKEY_ID); _trayIcon?.Dispose(); base.OnExit(e); } }}注释说明 - 使用NotifyIcon实现系统托盘注册CtrlShiftT作为全局热键。 - 通过Win32 APIRegisterHotKey确保热键在任意窗口下生效。 - 当热键触发时调用ShowTranslationWindow弹出翻译界面。## 实战代码示例2LLM翻译引擎封装翻译引擎是Rouyan的灵魂。以下代码封装了对OpenAI API的调用支持流式响应Stream以提高用户体验。csharpusing System;using System.Net.Http;using System.Text;using System.Text.Json;using System.Threading.Tasks;using System.Collections.Generic;namespace Rouyan.Services{ public class LlmTranslator { private readonly HttpClient _httpClient; private readonly string _apiKey; private readonly string _baseUrl https://api.openai.com/v1/chat/completions; // 可配置 public LlmTranslator(string apiKey) { _httpClient new HttpClient(); _apiKey apiKey; _httpClient.DefaultRequestHeaders.Add(Authorization, $Bearer {_apiKey}); } /// summary /// 使用LLM翻译文本支持流式输出 /// /summary /// param nametext待翻译文本/param /// param namesourceLang源语言如中文/param /// param nametargetLang目标语言如英语/param /// returns翻译后的字符串/returns public async Taskstring TranslateAsync(string text, string sourceLang, string targetLang) { // 构造系统提示词引导LLM进行精准翻译 var systemPrompt $你是一个专业翻译请将以下{sourceLang}文本翻译为{targetLang}只返回翻译结果; var requestBody new { model gpt-3.5-turbo, // 可替换为其他模型 messages new[] { new { role system, content systemPrompt }, new { role user, content text } }, temperature 0.3, // 低温度减少创造性提高准确性 stream false // 为简化使用非流式实际可设为true }; var json JsonSerializer.Serialize(requestBody); var content new StringContent(json, Encoding.UTF8, application/json); try { var response await _httpClient.PostAsync(_baseUrl, content); response.EnsureSuccessStatusCode(); var responseJson await response.Content.ReadAsStringAsync(); using var doc JsonDocument.Parse(responseJson); var result doc.RootElement .GetProperty(choices)[0] .GetProperty(message) .GetProperty(content) .GetString(); return result?.Trim() ?? string.Empty; } catch (Exception ex) { // 实际项目中应记录日志并通知用户 return $翻译失败{ex.Message}; } } /// summary /// 释放资源 /// /summary public void Dispose() { _httpClient?.Dispose(); } }}注释说明 - 构造函数接收API密钥并设置HTTP头。 -TranslateAsync方法构造包含系统提示的请求调用OpenAI的Chat Completion接口。 - 使用temperature0.3减少随机性确保翻译一致性。 - 异常处理返回错误信息便于调试。## 配置管理与用户设置Rouyan使用JSON文件存储配置例如config.jsonjson{ apiKey: sk-xxxx, sourceLanguage: 中文, targetLanguage: 英语, hotkey: CtrlShiftT}C#端通过ConfigurationBuilder读取csharpvar config new ConfigurationBuilder() .SetBasePath(AppDomain.CurrentDomain.BaseDirectory) .AddJsonFile(config.json, optional: false, reloadOnChange: true) .Build();var apiKey config[apiKey];用户可以通过设置窗口修改配置并保存回JSON文件。## 界面设计简洁与高效翻译窗口使用WPF XAML设计包含输入文本框、翻译按钮和结果显示区域。核心布局如下xmlWindow x:ClassRouyan.TranslationWindow TitleRouyan 翻译 Height300 Width400 WindowStyleNone ResizeModeNoResize TopmostTrue Grid Margin10 Grid.RowDefinitions RowDefinition HeightAuto/ RowDefinition Height*/ RowDefinition HeightAuto/ /Grid.RowDefinitions TextBox x:NameInputTextBox Grid.Row0 Height60 TextWrappingWrap AcceptsReturnTrue/ Button Grid.Row1 Content翻译 ClickTranslateButton_Click Height30 VerticalAlignmentTop/ TextBox x:NameOutputTextBox Grid.Row2 Height100 IsReadOnlyTrue TextWrappingWrap/ /Grid/Window后台代码绑定TranslateButton_Click事件调用LlmTranslator.TranslateAsync并将结果显示。## 部署与使用1. 编译项目生成可执行文件。 2. 将config.json和图标文件放在同一目录。 3. 运行程序系统托盘出现图标。 4. 按CtrlShiftT弹出翻译窗口输入文本并点击“翻译”即可。## 总结Rouyan是一个由WPF/C#驱动的轻量级翻译工具它利用LLM的强大能力在本地提供高质量翻译。通过本文的代码示例你可以看到如何 - 使用系统托盘和全局热键实现快捷唤醒。 - 封装LLM API调用实现灵活的翻译引擎。 - 通过配置文件实现用户自定义。未来可以扩展的功能包括支持更多语言、集成本地模型如Llama.cpp、添加历史记录、以及实现拖拽翻译。Rouyan不仅是一个工具更是探索LLM在桌面应用落地的实践案例。希望本文能激发你构建自己的LLM驱动小工具的灵感。