LibreTranslate 部署:自建翻译 API 实践
【免费下载链接】LibreTranslateFree and Open Source Machine Translation API. Self-hosted, offline capable and easy to setup.项目地址: https://gitcode.com/GitHub_Trending/li/LibreTranslate
上个月给产品做多语言功能评审时,我把某商业翻译 API 的按量单价拉出来算了一遍——按每月几百万次调用估算,账单数字让财务同事皱了下眉,而且客户文本要出境,合规那边也没点头。于是我转了自建路线,最后落在 LibreTranslate 上:一个开源翻译服务,翻译引擎是 Argos Translate,全程离线推理,数据不出本地机器,部署完调用费用就是零。
LibreTranslate 的翻译请求内部链路
这一节不介绍"它是什么",直接说清楚一次请求进来之后发生了什么,以及它和调云端 API 的本质区别在哪。
请求打到 Flask 应用后,先过一遍限流和 API 密钥校验(如果启用了的话),然后按语言对取出本地加载的模型,交给 Argos Translate 做神经推理——本质上就是一个本地跑的 NMT 模型,不是词表拼接,也不是转发给任何第三方。推理结果封装成 JSON 返回。整个链路不依赖外网,断网环境也能翻译;没有按量计费,因为根本没有"调用量"这个概念。
和常见方案比,关键差异只有三点:
| 维度 | LibreTranslate | 商业云端 API |
|---|---|---|
| 数据流向 | 文本留在本地,可跑在纯内网 | 文本上传到服务商服务器 |
| 调用成本 | 部署后零边际成本 | 按调用量计费 |
| 可用性依赖 | 不依赖外部网络(首次拉模型除外) | 依赖服务商服务状态 |
三种部署路径怎么选
先判断你的场景,再动手:只是快速验证或轻量生产,走 Docker ✅;要改代码、做定制(比如二次开发检测逻辑或限流策略),走源码;要长期挂着跑且不想依赖容器,走 systemd。
Docker:大多数人的默认选项
一条命令搞定,生产要点就三个:模型持久化到卷(不然容器重建又要重新下载)、restart 策略、API 密钥开关。
docker run -d --name libretranslate \ -p 5000:5000 \ -v lt_data:/home/libretranslate/.local \ -e LT_API_KEYS=true \ -e LT_API_KEYS_DB_PATH=/home/libretranslate/.local/api_keys.db \ --restart unless-stopped \ libretranslate/libretranslate:latest适用边界:如果你需要改libretranslate/app.py里的业务逻辑,Docker 官方镜像不够用,下面的源码路线更合适。
源码:要定制才值得走
什么情况下值得:你要魔改限流策略、给翻译结果加后处理、或者把翻译能力嵌进自己的 Python 服务里。定制化入口基本都在libretranslate/目录,app.py是核心,detect.py管语言检测。
git clone https://gitcode.com/GitHub_Trending/li/LibreTranslate cd LibreTranslate python -m venv venv && source venv/bin/activate pip install -e . python scripts/install_models.py python main.py --host 0.0.0.0 --port 5000systemd:长期裸机运行的兜底
只贴关键片段,完整的.service文件按 systemd 惯例写即可:
[Service] WorkingDirectory=/opt/LibreTranslate ExecStart=/opt/LibreTranslate/venv/bin/python /opt/LibreTranslate/main.py \ --host 0.0.0.0 --port 5000 Environment=LT_API_KEYS=true Restart=always核心 API 实测:翻译、检测、文件
我按实际使用频率调了三个端点:文本翻译、语言检测、文件翻译。Web 界面不用单独说——浏览器打开默认 5000 端口就能看到翻译页面,文本和文件两种模式都有,适合快速人工验证翻译质量。
/translate:单条和批量共用一个入口
q传字符串就是单条,传 JSON 数组就是批量,响应里translatedText的形态跟着变(字符串对字符串,列表对列表)。
curl -X POST http://localhost:5000/translate \ -d "q=Hello, world" -d "source=en" -d "target=zh" -d "format=text"{"translatedText": "你好,世界"}注意source传auto可以自动检测源语言;出错时返回 400 并带{"error": "..."}字段,限流触发是 429。
/detect:给一段文本猜语言
返回的是一个数组(按置信度排序),常用的是第一个元素:
curl -X POST http://localhost:5000/detect -d "q=Hola, mundo"[{"confidence": 0.9999, "language": "es"}]我在做批量文档预处理时基本只用它:先 detect 再翻译,省掉人工标注源语言。
/translate_file:multipart 上传,拿回文件 URL
参数是file+source+target,响应不是文件内容本身,而是translatedFileUrl,再去 GET 那个路径下载:
curl -X POST http://localhost:5000/translate_file \ -F "file=@notes.txt" -F "source=en" -F "target=zh"{"translatedFileUrl": "/translated-files/xxxxxxxx"}这个端点可以关掉(--disable-files-translation),纯文本 API 服务的话建议关,少一个攻击面。
生产部署安全与性能清单
上生产前我逐项核对的清单。每项都给了"为什么",命令只保留核心一行;所有参数既能用命令行传,也能用LT_前缀环境变量传。
安全
- 启用 API 密钥(
LT_API_KEYS=true)——不加密钥,局域网里谁都能白嫖你的推理资源 - 设限流(
LT_REQ_LIMIT=100)——防单个客户端用长文本请求打满 CPU - 前置 Nginx 终止 SSL——API 密钥走明文 HTTP 等于白送,应用层自带
--ssl参数也可以,但反代还能顺带做访问日志和缓冲
性能
- 线程数(
LT_THREADS=4)——默认 4,按 CPU 核心数调,直接影响并发推理能力 - 按需加载模型(
LT_LOAD_ONLY=en,zh,fr)——全量加载很吃内存,只装你真用的语言对 - 开翻译缓存(
--translation-cache all)——重复文本直接命中缓存,长尾收益明显
生产拓扑我保持得很简单,四个节点:
三个真实场景的实现片段
给现有 Web 应用加多语言切换
要解决的问题:页面上几十条固定文案,要跟着用户选的语言实时换。核心就是攒一批q调一次接口,批量模式一次换完:
async function switchLang(target) { const nodes = document.querySelectorAll('[data-i18n]'); const res = await fetch('http://localhost:5000/translate', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({q: [...nodes].map(n => n.textContent), source: 'en', target, format: 'text'}) }); const {translatedText} = await res.json(); nodes.forEach((n, i) => n.textContent = translatedText[i]); }注意两点:同文本同语言对结果稳定,前端可以再挂一层缓存省请求;批量条数受--batch-limit约束,超长文案记得分批。
批量文档翻译小工具
要解决的问题:一份几百行的文本文件整体翻掉,带进度、能扛限流:
import requests, time API = 'http://localhost:5000/translate' texts = [l.strip() for l in open('src.txt') if l.strip()] for i, t in enumerate(texts): r = requests.post(API, data={'q': t, 'source': 'auto', 'target': 'zh', 'format': 'text'}) if r.status_code == 429: time.sleep(60) # 触发限流,等一分钟重试本条 continue print(f"[{i+1}/{len(texts)}] {r.json()['translatedText']}")实测下来这是最容易翻车的场景:LT_CHAR_LIMIT和LT_REQ_LIMIT会先于你的耐心生效,长文档先按段落切、速率拉低比什么都管用。
CI 流水线里的本地化冒烟检测
要解决的问题:部署流水线里确认翻译服务活着、翻译端点真能出结果,而不是只看端口通不通:
set -e curl -sf http://lt.internal:5000/health > /dev/null RESULT=$(curl -s -X POST http://lt.internal:5000/translate \ -d "q=health check" -d "source=en" -d "target=zh" -d "format=text") echo "$RESULT" | grep -q '"translatedText"' && echo "i18n service OK"一句固定文本走完/translate,比单独探/health更能覆盖模型加载是否正常;开了密钥的话 CI 里记得带上。
踩坑记录:本地部署容易碰的四个坑
模型下载超时。第一次启动卡很久甚至报错,/languages返回的语言数偏少——Argos 模型动辄几百 MB,网络差时下载容易断。解法:配置LT_UPDATE_MODELS=true(对应--update-models)让它在启动时增量补齐,中断重跑即可,不必整包重来。
内存占用超预期。⚠️ 我一开始按 2GB 规划,实际起来后吃了 3G 多——原因是默认会加载全部已安装语言对的模型,每个语言对一套。加一个--load-only en,zh,fr把语言对收窄,内存立刻降下来,这是最立竿见影的一刀。
端口冲突。容器反复 restart,日志里是 Address already in use——5000 端口被别的占用了。lsof -i :5000找到占用进程,或者直接把映射改成-p 5001:5000,一行搞定。
响应形态变了取到空值。⚠️ 单条调用正常的客户端代码,切到批量模式后translatedText变成了 list,原来直接当字符串用的地方全崩。我一开始也搞错了,后来统一成"客户端永远发数组、永远收数组",用一个类型判断收口,代码反而更简单。另外出错时别硬取translatedText,先判状态码和error字段。
该不该选它:几条判断标准
自建翻译 API 不是无条件划算,我按这几个标准来分:
- 数据合规要求文本不能出内网/出境 → 自建几乎没有替代选项,LibreTranslate 合适;
- 日均调用量在百万次以内、语言对相对集中 → 自建成本优势明显,商业 API 的账单在这里才是大头;
- 需要 100+ 语言覆盖或对标一线云厂商的翻译质量 → 老实选商业 API,开源模型的长尾语言质量还有差距,它覆盖的 50+ 种语言够用但别指望面面俱到。
项目演进方向上简单提一句:
- 更好的 NMT 模型持续集成,质量在逐版本改善
- 社区驱动的语言库扩展
- 多模态(文档、图片)等能力在路线上,当前核心仍是文本
下一步建议很具体:先用 Docker 把最小部署跑起来,拿你真业务里的语言对测延迟和准确率——单条几百毫秒级、质量人工抽检能接受,再谈生产化的密钥、限流和多实例;达不到就先别上,别在第一天就把生产架构搭满。
【免费下载链接】LibreTranslateFree and Open Source Machine Translation API. Self-hosted, offline capable and easy to setup.项目地址: https://gitcode.com/GitHub_Trending/li/LibreTranslate
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考