「边打字边开挂」:一个 AutoHotkey 实时翻译输入器的诞生记
当你还在中英来回切换、复制粘贴发疯的时候,有人已经一边敲中文一边在全球队友面前流利输出英文 trash talk 了——而且手都没离开键盘。
这篇文章,讲的就是这样一个小而精但极具实用价值的项目:Real-time-translation-typing —— 一个基于 AutoHotkey 的实时打字翻译工具,支持键盘输入、语音输入、LOL 游戏内聊天联动,还能自由切换多种在线翻译服务。
别看项目体量不算大,里面藏着的技术思路却一点不简单:
-
前端界面用 Direct2D 自绘输入框和提示气泡,轻量却灵活。
-
与翻译引擎之间通过 ZeroMQ 做进程间通信,主进程负责 UI 和输入捕获,子线程/子进程专职翻译。
-
支持多家翻译 API 和「网页翻译爬取」,做了一个小型的「翻译聚合器」。
-
利用 Windows 原生 API(IMM、UIAutomation 等)精确获取光标位置、控制输入法候选框。
-
集成 WebView2 做语音识别输入,并为 LOL 这种特殊场景做了专门适配和协议封装。
如果你对下面这些场景有共鸣:
-
写英文邮件时一边想一边切翻译工具,思路被打断;
-
和老外打游戏,别人打字如飞,你还在「复制→翻译→复制→粘贴」;
-
想语音说中文,电脑自动帮你变成英文发出去;
那这个小工具,值得你认真看完这篇文章。
接下来,我们从背景、架构、核心实现、使用方式和应用场景,一步步拆开这个项目,看看作者到底玩了多少「骚操作」。
一、项目背景:为什么要造一个「实时打字翻译器」?
先把话说直白一点,这个项目要解决的问题,其实就是:
打字用中文思考,输出却要是英文(或其他外语),而且要尽量不中断当前操作。
很多人多少都经历过这种体验:
-
聊天软件里:你写中文 → 复制 → 打开浏览器 → 粘贴 → 翻译 → 再复制 → 回聊天框 → 粘贴 → 发送。
-
写英文邮件:要在中文和英文、各类翻译网站之间频繁切换,效率感人。
-
游戏里:节奏飞快,你还在窗口间来回切换,队友已经团灭两波了。
这种流程有几个明显痛点:
-
打断思路:每一次 Alt+Tab,都是对专注度的一次谋杀。
-
操作繁琐:明明只是想翻译一句话,却像在做多任务操作练习题。
-
不够「实时」:翻译和输入是两个完全割裂的动作,很难做到边打边看结果。
于是,这个项目的核心立意就很清晰:
把「翻译」这一动作,彻底嵌进「打字」本身,让翻译像输入法一样无感存在。
这也是为什么项目名字叫 Real-time-translation-typing——不是「翻译工具」,而是一个更偏「输入方式增强」的小系统。
项目目前主要有两种形态:
- API 版本(
实时打字翻译.ahk + thread.ah2):-
主进程捕获输入、绘制界面;
-
子线程/子进程负责调用翻译 API,返回结果后展示并写入剪贴板。
-
- 网页版/WebView2 版本(
实时打字翻译-API.ah2):-
通过 WebView2 直接控制在线翻译页面,模拟输入、读取结果;
-
同时集成语音识别 HTML 页面(
lib/语音.html),支持语音转文字再翻译; -
对 LOL 等游戏场景做了专门快捷键和逻辑适配。
-
API 版结构更清晰、逻辑更偏「后端 + 客户端」,网页版则更贴近「浏览器自动化 +富交互 UI」。两者都围绕一个核心目标:尽量减少翻译这件事本身的存在感。
二、总体技术架构:一个轻量级「输入增强系统」
先用一句话概括这个项目的架构:
一个基于 AutoHotkey 的桌面输入增强层,上层对接各种应用(聊天工具、游戏、编辑器),下层对接多种翻译服务,中间用 ZeroMQ 做异步通信,辅以 Direct2D 和 WebView2 提供 UI 和网页能力。
从代码结构看,大致可以分成以下几个模块:
-
输入捕获与 UI 展示(前台)
-
文件:
实时打字翻译.ahk、实时打字翻译-API.ah2 - 技术点:
-
键盘钩子、全局热键(
Hotkey、OnMessage)。 -
通过
GetCaretPosEx获取当前文本光标位置或鼠标位置。 -
使用
Direct2DRender绘制自定义输入框(Edit_box类),并根据文本长度调整大小。 -
通过
ToolTip/btt显示当前翻译 API 名称和翻译结果。
-
-
-
翻译请求发送与结果接收(消息层)
- API 版:
-
使用
zmq作为通信库,主进程持有ZMQ_PUBsocket,子线程/进程持有ZMQ_SUB。 -
主进程把当前文本、光标坐标、是否 IME 输入、当前翻译 API 等信息打包为 JSON,发送给
thread.ah2。
-
- Web 版:
-
通过各个
Sougou_web_cd/Youdao_web_cd/Deepl_web_cd的包装类,直接向 WebView2 容器中的翻译网页注入 JS,实现「模拟输入 → 监听输出」。
-
- API 版:
-
翻译执行模块(后台)
- API 版:
-
文件:
thread.ah2 - 根据 config 中选中的 API,执行对应翻译逻辑:
-
有道:HTTP POST + JSON 解析。
-
谷歌:调用公开接口
https://translate.googleapis.com/translate_a/single。 -
搜狗:请求网页,解析 HTML DOM 提取翻译结果。
-
百度:签名 + 请求官方开放平台接口。
-
Edge:调用
api-edge.cognitive.microsofttranslator.com的接口。
-
-
翻译完成后通过
ToolTip显示,并覆盖剪贴板,方便粘贴。
-
- Web 版:
-
翻译完全由网页自身负责;AHK 只负责把输入丢给页面、把结果抓回来。
-
- API 版:
-
配置与扩展
-
配置文件:
config/setting.json - 内容包括:
-
当前主用翻译源
cd。 -
可用 API 列表
all_api。 -
每个 API 是否开启、是否支持实时翻译、以及必要的秘钥信息(如百度)。
-
-
通过
tab或~tab快捷键实现在不同 API 之间切换。
-
-
语音输入与游戏集成(主要在 Web 版)
- 语音输入:
-
lib/语音.html使用浏览器的 Web Speech API(或同类方案),实时识别语音为文字。 -
AHK 监控该页面输出,并把结果回填到
Edit_box。
-
- LOL 集成:
-
利用
XButton1/XButton2作为语音触发和结束键。 -
检测
RiotWindowClass窗口,把翻译后的结果通过模拟键盘输入发送到游戏内聊天框。 -
支持
/all模式,并通过 ZeroMQ 将消息发送到一个后端服务进行进一步处理(如转发到其他渠道)。
-
- 语音输入:
可以看到,这个项目虽然基于 AutoHotkey,但其设计思路已经远超「几个热键脚本」的范畴,更像是一个 小型输入系统:
-
它有 UI 层(Direct2D + ToolTip)。
-
有通信层(ZeroMQ)。
-
有服务层(翻译 API/Web 端)。
-
有配置层(
setting.json决定策略)。 -
还有针对具体应用的适配层(LOL、任意窗口、剪贴板模式等)。
三、核心实现拆解:从按下 ALT+Y 到翻译结果出现
理解一个工具,最好的方式就是跟着一次完整的调用链走一遍。我们以 API 版 为例,看下从用户按下快捷键到看到翻译结果,中间具体发生了什么。
3.1 热键与输入入口:fanyi()
在 实时打字翻译.ahk 里,入口函数 main() 中设定了多组热键:
-
Alt+Y:打开输入框,进入翻译输入模式; -
Alt+Enter:对当前翻译结果发音; -
Enter:输出翻译结果文本; -
Ctrl+Enter:输出原始文本; -
Tab:切换翻译 API; -
Esc:关闭输入框。
以 Alt+Y 为例,它绑定了 fanyi():
Hotkey('!y', (key) => fanyi())
fanyi() 的主要逻辑:
-
通过
GetCaretPosEx(&x, &y, &w, &h)尝试获取当前文本光标位置; -
如果失败,则回退到当前鼠标位置
MouseGetPos(&x, &y); -
记录当前坐标为全局
g_cursor_x / g_cursor_y; -
根据当前 API 的实时配置,弹出一个 ToolTip 提示当前翻译源;
-
在光标附近
g_eb.show(x, y)显示自绘输入框; -
调用
g_eb.draw()进行初次绘制。
体验上的感觉就是:
-
不用切窗口,不用切输入法,在任何文本输入位置,按下 Alt+Y,一个悬浮输入框就出现在光标下方。
3.2 自绘输入框:Edit_box 类
输入框并不是普通 Windows 控件,而是基于 Direct2DRender 的自绘组件。这么设计有几个好处:
-
完全不依赖目标应用的 UI 风格;
-
可以精确控制尺寸、位置、颜色、字体和透明度;
-
可以实现一些小功能,比如绘制插入位置、背景高亮等。
构造函数大致如下:
class Edit_box
{__New(x, y, w, h) {this.ui := Direct2DRender(x, y, w, h,,, false) ; API版中this.text := ''}
}
绘制方法 draw(flag := 0) 的关键逻辑:
-
调用
GetTextWidthHeight(this.text, 20)计算当前文本的绘制宽高; -
根据内容动态调整窗口大小:
this.move(x, y, wh.width + 100, wh.height + 100); - 绘制背景矩形和边框:
-
深色半透明背景
0xcc1E1E1E; -
红色描边
0xffff0000;
-
-
绘制文本本身,颜色
0xFFC9E47E(偏黄的高亮色,非常符合「工具提示」那种感觉)。
在 Web 版中,输入框还多了一条绿色的竖线,用于表示当前插入位置:
ui.DrawLine(wh.width - last_txt_wh.width, 0,wh.width - last_txt_wh.width, wh.height, 0xAA00FF00)
这段逻辑结合 insert_pos(从文本末尾往回的偏移量)实现了简单但相当实用的「光标定位」。
3.3 输入捕获和 IME 适配:ON_MESSAGE_WM_CHAR / ON_MESSAGE_WM_IME_CHAR
为了让输入框真正像一个「迷你输入法」,项目监听了 WM_CHAR 和 WM_IME_CHAR 消息:
OnMessage(WM_CHAR := 0x0102, ON_MESSAGE_WM_CHAR)
OnMessage(WM_IME_CHAR := 0x0286, ON_MESSAGE_WM_IME_CHAR)
-
WM_CHAR:主要用来处理普通键盘字符; -
WM_IME_CHAR:处理输入法候选确定后的字符(比如中文拼音选字后回填的汉字)。
二者最终都会调用 g_eb.set_imm(a[1]),而 set_imm() 中:
-
调用
ImmGetContext、ImmSetCompositionWindow、ImmSetCandidateWindow等 API,重定向输入法候选框位置到自定义输入框区域; -
把输入字符转换成 UTF-16 字符串,通过
push()拼接到当前文本; -
调用
draw()重新渲染输入框。
换句话说,不论你是英文键盘直接敲,还是用中文输入法,都被统一纳入到这个自绘输入框里处理。
这一步是整个项目的「体验关键点」之一:
-
不劫持全局输入法,不强改系统行为;
-
只是临时在当前激活窗口旁边开了一个虚拟输入层。
3.4 何时触发翻译:实时 vs 空格触发
Edit_box.draw() 在每次更新时,会根据当前 API 的策略决定是否立即调用翻译:
-
如果是
youdao,基本是实时触发; -
对于有调用配额限制的百度、可能需要翻墙的谷歌,是否实时则由配置项
is_real_time_translate决定; -
sougou这种网页爬虫型翻译,因为延迟较高,一般更适合非实时触发; - 还有一种是「空格触发」模式:
-
当最后一个字符是空格时,才把整段文本发送给后台翻译。
-
触发的方式很简单:
- API 版:调用
cd(this.text, x, y)-
将文本和光标位置打包成 JSON,通过 ZeroMQ PUB socket 发送;
-
- Web 版:调用对应翻译页面封装类:
-
this.sg.set_input_box(input_text)/this.yd.set_input_box(...)/this.dp.set_input_box(...)。
-
这个设计有一个非常实用的点:
不同翻译源的「成本」不同,策略也不同。
比如:
-
有道:速度快、成本低,可以实时;
-
百度:有配额限制,不适合每个字都发;
-
搜狗:网页爬虫,速度慢,不实时也更自然。
这些策略都被统一收敛到了 setting.json / 内部逻辑中,对用户来说只表现为:
-
你切到不同 API,体验会有细微差别,但行为都在预期之内。
3.5 后台翻译执行:thread.ah2
thread.ah2 的结构也很清晰:
-
从
A_Args[1]中取到 ZeroMQ 上下文ctx; -
创建
ZMQ_SUBsocket,连接到inproc://main; -
周期性
zmq_recv_string收取消息; -
JSON 解析出
text、api、坐标等信息; - 根据
api调用对应的翻译函数:-
youdaocd(text) -
baiducd(text, appid, appsec, from, to) -
googlecd(text) -
sougoucd(text) -
edgecd(text)
-
-
最后通过
ToolTip在原先位置显示翻译结果,并把结果写入A_Clipboard。
例如 Google 翻译函数:
googlecd(keyword, time_out := 2)
{from := 'en', to := 'zh-CN'if(RegExMatch(keyword, "\p{Han}"))from := 'zh-CN', to := 'en'URL := Format('https://translate.googleapis.com/translate_a/single?dt=t&client=gtx&sl={1}&tl={2}&dj=1&ie=UTF-8&oe=UTF-8&q={3}',from, to, EncodeDecodeURI(keyword))WebRequest := ComObject("WinHttp.WinHttpRequest.5.1")WebRequest.Open("GET", url, true)WebRequest.SetRequestHeader("User-Agent", "Mozilla/5.0 ...")WebRequest.Send()try {WebRequest.WaitForResponse(time_out)result := WebRequest.ResponseTexto := JSON.parse(result)result := o['sentences'][1]['trans']} catch as e {result := ''}return result
}
搜狗翻译则比较「接地气」,走的是 HTML 解析路线:
-
GET 请求
https://fanyi.sogou.com/text?keyword=...; -
把 ResponseText 写入
htmlfileCOM 对象; -
用
querySelector找到#trans-result或#trans-result > span.trans-sentence; -
读取
innerHTML作为翻译结果。
这种实现方式虽然不是最「正规」、最 API 化的方案,但在桌面工具场景里,非常实用且好维护。如果有一天搜狗改了页面结构,改下选择器就能继续用。
3.6 输出阶段:发音、粘贴、原文/译文切换
当翻译结果准备就绪后,用户有几种常见操作路线:
- 直接粘贴翻译结果:
Enter⇒ 调用send_command('translate'):-
把
g_eb.fanyi_result写入剪贴板; -
激活目标窗口(比如之前光标所在窗口);
-
模拟
{RShift Down}{Insert}{RShift Up}即「Shift+Insert」粘贴。
-
- 粘贴原始文本(不翻译):
Ctrl+Enter⇒send_command('Primitive'):-
用原文填充剪贴板,再粘贴。
-
- 发音:
Alt+Enter⇒sound_play():-
判断文本是否包含汉字;
-
英文用有道发音接口
https://dict.youdao.com/dictvoice?audio=...; -
中文用另一个 TTS API
https://api.oick.cn/txt/apiz.php?text=...&spd=10; -
通过
PlayMedia封装的 WinRTMediaPlayer自行播放,不依赖外部播放器。
-
整个过程下来,用户只感觉到:
-
打了一段中文;
-
输入框里出现了翻译结果;
-
按下 Enter;
-
目标应用中就像你亲手打出那段英文一样。
而在背后,已经经历了:输入捕获 → 自绘渲染 → 进程间通信 → HTTP 请求 → JSON/HTML 解析 → 结果回传 → 剪贴板设置 → 模拟输入。
四、Web 版与语音输入:当翻译器开始「听你说话」
如果说 API 版更像「程序员型工具」,那么 Web 版则是把「能用就行」发挥到了极致。
4.1 WebView2:让翻译页面成为你的「内嵌引擎」
在 实时打字翻译-API.ah2(实际上是 Web 版主脚本)中,可以看到项目引用了:
#include <WebView2>
#include ./utility/网页翻译集合.ah2
网页翻译集合.ah2 里封装了对搜狗、有道、百度、Deepl 等网页的自动化控制逻辑,整体思路是:
-
用 WebView2 加载相应的在线翻译网站;
-
找到页面中的输入框和输出区域;
-
通过 JS 注入/执行,设置输入框的值、模拟输入事件;
-
监听输出区域文本变化,通过回调通知 AHK 主脚本。
在 Edit_box.__New() 中,可以看到类似这样的初始化:
if(this.has_key('sougou')) {this.sg := sg := Sougou_web_cd()sg.set_out_change_call_back(this.on_change.bind(this))sg.set_input_box('我来自搜狗')
}
这表示:
-
如果当前全局可用翻译源中包含
sougou,就构造一个搜狗翻译控制器; -
设置输出变更回调
on_change; -
初始化时塞一句「我来自搜狗」,方便在 WebView2 界面中验证联通情况。
只要实现若干个 {xxx}_web_cd() 类,就能无缝把新的网页翻译源接入系统,完全不改主流程,扩展性非常好。
4.2 语音输入:lib/语音.html + SoundINput
语音输入是 Web 版的亮点之一:
global g_sound := SoundINput(A_ScriptDir '\lib\语音.html')
g_sound.set_out_change_call_back(sound_call_back)
配套的回调:
sound_call_back(name, text)
{g_eb.set_text(text)g_eb.draw()
}
流程很简单:
-
打开内嵌 WebView2,加载
语音.html; -
HTML 内部通过浏览器的语音识别能力,把语音转成文本;
-
每次文本更新时,通过某种桥接机制(通常是 WebView2 的 postMessage 或 URL 协议)把内容回推给 AHK;
-
AHK 接到回调后,更新输入框内容并触发翻译。
用户体验层面就是:
-
按下
Alt+I:弹出输入框并开启语音识别; -
开始说中文 → 输入框文字变化 → 触发翻译 → 结果准备好;
-
按 Enter:翻译后的内容被送进当前窗口或游戏。
4.3 LOL 专属适配:玩游戏也要优雅地「发言」
在 Web 版脚本中还有一块专为 LOL 准备的逻辑:
HotIfWinExist("ahk_class RiotWindowClass")Hotkey('XButton1', (key) => input_sound())Hotkey('XButton2', (key) => send_command('Primitive'))Hotkey('!XButton2', (key) => (g_eb.text := '/all ' g_eb.text, send_command('Primitive')))Hotkey('^XButton2', (key) => (g_eb.text := '/all ' g_eb.text, g_eb.fanyi_result := '/all ' g_eb.fanyi_result, send_command('')))Hotkey('+XButton2', (key) => send_command(''))Hotkey('^f8', (key) => switch_lol_send_mode())
HotIf()
配合 SendCn 和 sendcmd2game 两种模式:
-
一种是模拟用户在游戏里逐字键入(防止部分游戏对粘贴行为的限制);
-
另一种是通过 ZeroMQ 把聊天内容发送给外部服务,用于记录或延展功能。
这部分逻辑细节很多,但总体目标只有一个:
让你在游戏中也可以像在聊天软件里一样自然地用中文思考、用英文输出,而且操作尽量不打断游戏节奏。
五、配置与使用:从「能跑」到「好用」的细节
项目的配置主要集中在 config/setting.json:
{"cd" : "edge","all_api" : ["baidu", "youdao", "sougou", "google", "edge"],"edge": {"is_open" : 1,"is_real_time_translate" : 1},"youdao": {"is_open" : 0},"baidu" : {"is_open" : 0,"BaiduFanyiAPPID" : "","BaiduFanyiAPPSEC": "","is_real_time_translate" : 0},"sougou" : {"is_open" : 1,"is_real_time_translate" : 0},"google": {"is_open" : 0,"is_real_time_translate" : 0}
}
5.1 API 管理与切换
-
cd:当前主用翻译源,例如edge、youdao等; -
all_api:允许参与轮换的翻译源; - 每个翻译源都可以配置:
-
is_open:是否启用; -
is_real_time_translate:是否每次输入变化就立即翻译。
-
键盘上只需要记住一个操作:
-
Tab/~tab:在当前启用的翻译源之间轮换。
项目内部是这样做的:
for k,v in g_config['all_api'] {if(v = g_current_api) {current_index := kbreak}
}loop g_config['all_api'].Length {current_index++if(current_index > g_config['all_api'].Length)current_index := 1api := g_config['all_api'][current_index]if(g_config[api]['is_open'] && g_current_api != api) {g_current_api := apibreak}
}
逻辑很直观:
在
all_api里按顺序找下一个is_open == 1的 API,作为当前翻译源。
5.2 常用快捷键一览
从 README 和代码里可以整理出一组「日常够用」的快捷键:
-
Alt + Y:打开输入框(键盘输入模式)。 -
Alt + I:打开输入框并开启语音输入(Web 版)。 -
Enter:输出翻译文本到当前窗口。 -
Ctrl + Enter:输出原始文本(不翻译)。 -
Alt + Enter:朗读翻译结果。 -
Tab:切换翻译 API。 -
Esc:关闭输入框。 - Web 版额外:
-
Ctrl + C:复制当前翻译结果。 -
Ctrl + V:将剪贴板文本填入输入框。 -
Ctrl + Alt + Y:直接翻译剪贴板内容。 -
XButton1/XButton2:在 LOL 中启动/结束语音输入或发送内容。
-
5.3 运行环境与启动方式
根据 README:
- Web 版推荐环境:
-
Windows 10 或安装了 WebView2 Runtime。
-
AutoHotkey v2H 版本(用于支持增强特性)。
-
- 启动方式之一:
-
把
实时打字翻译-网页版(推荐).ah2拖到AutoHotkey.exe上执行; -
或者重命名为
.ahk,再配合改名后的AutoHotkey.exe直接双击运行。
-
API 版相对更偏折腾,适合对 AHK 有一定基础的用户:
-
确保依赖库(
zmq、Direct2DRender等)可用; -
启动主脚本
实时打字翻译.ahk,确保thread.ah2能正常被Worker执行。
六、典型应用场景:从工作到游戏,全面提效
这个工具的「适用面」其实比初看时要广得多,这里列几个典型场景,方便你对号入座。
6.1 写英文邮件/文档:
场景:
-
在 Outlook、浏览器邮件客户端、Word、VS Code 甚至记事本里,都可以直接用这个输入框辅助写作。
玩法:
-
在任何编辑区域按
Alt+Y; -
中文思考 → 中文输入;
-
一边打字一边看悬浮输入框里的英文翻译;
-
合适时按 Enter,翻译结果被填入正文;
-
对于反复出现的专有名词,可以用
Ctrl+Enter保留原文。
这比切换到浏览器找翻译网站要顺滑得多,特别适合「半熟练」的英文用户。
6.2 实时聊天:
场景:
-
和海外同事/朋友在微信 PC、QQ、Telegram、Discord 等各种 IM 里聊天。
玩法:
-
对话框中光标就绪;
-
按
Alt+Y; -
中文输入 → 实时翻译;
-
按 Enter 把翻译结果填充进去,再按一次 Enter 发送。
没有频繁窗口切换,没有待办遗失感,只有顺畅的沟通体验。
6.3 游戏场景(LOL 等):
场景:
-
和外国队友一起排位,又想表达清楚信息,又不想拖队友节奏。
玩法:
-
游戏中按
XButton1(鼠标侧键)启动语音输入; -
说中文:「小心对面打野在上路草丛」;
-
语音转文字 → 翻译 →
XButton2发送; -
队友收到的是一条流畅的英语提示。
你可以像队内指挥一样说中文,电脑帮你把它变成别人的母语。
6.4 编程辅助:变量命名小助手
项目顺手还塞了一个很实用的小功能:
Hotkey("+!enter", (key) => serpentine_naming('hump')) ; 驼峰命名
Hotkey("^!enter", (key) => serpentine_naming('snake')) ; snake 命名
流程:
-
输入框内先写中文描述,比如「用户注册时间」;
-
翻译成英文(比如
user registration time); - 按
Shift+Alt+Enter或Ctrl+Alt+Enter生成:-
驼峰:
UserRegistrationTime; -
蛇形:
user_registration_time;
-
-
结果自动放入剪贴板并输入到编辑器中。
说白了,这是一个 「中→英→规范变量名」的快速通道,非常适合写业务代码时使用。
七、从这个项目里能学到什么?
如果你只是想「用」,看到这里基本已经够用了。但从开发者角度来看,这个项目还种下了好几个值得借鉴的思路。
7.1 不要小看「脚本语言」做桌面增强的力量
很多人提到 AutoHotkey,会下意识觉得它只是:
-
映射个快捷键;
-
做点窗口自动化;
-
写点小宏脚本。
但这个项目告诉你:
当你把 AHK 当作「桌面应用框架」用时,它也能写出结构清晰、可扩展的工具。
关键点包括:
-
自定义类、模块化拆分(
Edit_box、Zmq_send2plug等); -
与 C/C++ DLL、Win32 API 深度交互(IMM、UIAutomation);
-
单独的配置层(
setting.json),让功能和策略解耦; -
借助 ZeroMQ 等外部库实现跨进程通信。
这种组合拳,使得 AHK 不再是「简单脚本」,而是一个可以快速原型化桌面工具的平台。
7.2 输入法级别的工具,核心是「不打扰」
整个项目设计里,有一个贯穿始终的哲学:
让用户尽可能少地感知到工具的存在,但又实打实地提高效率。
比如:
-
不抢输入焦点,只在附近开一个小输入框;
-
不替代系统输入法,而是临时包裹一层;
-
翻译结果直接进入剪贴板,然后模拟输入,就像你自己打出来一样;
-
关闭时自动清空状态、隐藏 Tooltip,不留下「视觉残留」。
这类设计非常值得做工具产品的人借鉴。
7.3 多翻译源聚合的扩展思路
项目用了一套非常轻量的「翻译聚合」模式:
-
每个翻译源有自己的配置块;
-
选择和轮换逻辑与翻译具体实现解耦;
- 新增翻译源的步骤大致是:
-
增加配置项;
-
写一个
{api_name}cd函数(API 版)或{ApiName}_web_cd类(Web 版); -
在切换逻辑里加入相应 case 即可。
-
这种模式也很适合作为其他「聚合型工具」的参考,比如:
-
聚合搜索;
-
聚合 AI 模型调用;
-
聚合字典/百科等。
八、未来可以怎么进化?
最后,稍微展开想象一下,这个项目在现有基础上,很容易拓展出不少有趣方向。
8.1 引入本地/大模型翻译
目前项目主要依赖在线翻译:有道、谷歌、Edge、搜狗、百度等。如果结合现在的大模型生态,可以考虑:
- 支持调用本地或云端大模型(如 GPT 系列、国内大模型 API),执行:
-
上下文感知翻译;
-
语气润色;
-
简历/邮件的专业表达优化。
-
只要保持 ZeroMQ 这层通信接口不变,翻译后端就可以从「多家翻译 REST 接口」升级成「统一的智能语言服务」。
8.2 自定义翻译风格与术语库
在技术、产品、游戏等细分领域,专有名词和表达风格尤为重要。可以在现有配置体系上扩展:
-
支持用户维护自己的术语词典;
-
支持「翻译后自动替换部分短语」;
-
支持「正式/口语/幽默/严谨」等风格偏好,从而让输出更贴合不同场景。
8.3 更丰富的 UI 与状态反馈
现在界面已经比较克制了,但仍可以考虑:
-
在输入框里同步显示原文与译文;
-
支持多行翻译上下文;
-
对翻译失败、网络异常给出更明确提示;
-
给每种翻译 API 设置不同的配色或小图标,切换时更直观。
8.4 插件化架构
目前翻译源、特定应用(如 LOL)都是以「硬编码」的形式集成在工程里。长期看,也可以升级为:
- 定义一套插件协议:
-
输入:原文、上下文、目标语言;
-
输出:译文、可选解释信息;
-
插件通过某种 IPC/HTTP 协议跟主程序通信;
-
- 这样任何人都可以开发自己的翻译/处理插件,比如:
-
翻译后自动检查语法;
-
翻译后顺便生成反问句、延伸表达;
-
专门进行术语审核。
-
九、结语:把翻译,悄悄变成「输入体验升级」
回到一开始的问题:
为什么要造这么一个「实时打字翻译器」?
看完代码你会发现,作者其实不是单纯地想做一个「给你翻译结果」的工具,而是更在意:
-
你在写东西时的流畅感;
-
你在游戏里沟通时的节奏感;
-
你在中英文世界间切换时的自然度。
它做的事情,说白了就一句话:
帮你用中文思考,用英文(或其他外语)表达,而且尽量不打扰你原本的操作节奏。
从工程角度,这个项目不是什么「高大上架构」,也不是动辄几万行代码的大工程,但它用:
-
恰到好处的技术选型;
-
细腻的交互设计;
-
对真实使用场景的充分理解;
打造出了一个足够「贴身」的小工具。
如果你经常在中英文之间来回奔波,或者喜欢和全球玩家一起开黑,那不妨试试把这个项目跑起来,让它悄悄住进你的键盘,替你承担那些无聊又机械的操作。
毕竟,在这个时代,能帮你省下 Alt+Tab 的工具,都值得认真对待。
更多AIGC文章
RAG技术全解:从原理到实战的简明指南
更多VibeCoding文章
更多Agent文章

