前言

相信很多人都用过 Zotero 的 Translate for Zotero 这个翻译插件,但是它自带的 Google 翻译能力很糟糕,跟网页版的 Google 翻译效果差远了。

所以很多人会使用一些进阶的设置,通过 API 接入各家的大模型,翻译效果会好很多。不过各家大模型能力参差不齐,比如百度、腾讯的翻译能力就一般,机翻味儿很重。各家 API 的调用次数限制也各不相同,像百度的普通翻译接口,每个月前 50 万个字符(约 25 万到 30 万个汉字)是免费的,超过了就需要付费。我先前也接了一个 DeepSeek 的 API,按量收费的,百万 tokens 两毛钱,效果还行,就是不知道为什么速度很慢。

最近看到 Google 云盘有一个 Apps Script 的功能,碰巧它有内置的 LanguageApp.translate() 函数,正好拿来试试。效果非常 amazing 啊,跟网页版 Google 翻译效果完全一样,比 DeepSeek 效果好,速度也更快。我看了一下官方文档,它这个限制是每天 5000 次的请求,每次上限 10000 个字符,相比之下还是蛮划算的,个人用是完全足够了。

部署方法

首先我们打开 Google 云端硬盘,点击左上角“新建 - 更多 - Google Apps Script”,点击创建脚本,我们将这串代码粘贴进去,然后保存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
function doPost(e) {
var rawData = e.postData.contents;
var payload = {};
var debugMode = false;

try {
payload = JSON.parse(rawData);
// 调试模式
if (payload.debug === true || payload.debug === "true") {
debugMode = true;
}
} catch (err) {
return createResponse("JSON 解析错误: " + err.toString());
}

// 获取提示词内容 (${sourceText}--->${langTo})
var fullContent = "";
if (payload.messages && payload.messages.length > 0) {
fullContent = payload.messages[payload.messages.length - 1].content;
}
var sourceText = "";
var langTo = "zh-CN";
if (fullContent.indexOf("--->") !== -1) {
var splitIndex = fullContent.lastIndexOf("--->");
sourceText = fullContent.substring(0, splitIndex).trim();
langTo = fullContent.substring(splitIndex + 4).trim();
} else {
sourceText = fullContent;
}

// 执行翻译
var translatedText = "";
try {
if (sourceText) {
translatedText = LanguageApp.translate(sourceText, '', langTo);
} else {
translatedText = "未获取到待翻译文本";
}
} catch (err) {
translatedText = "Google 引擎报错: " + err.toString();
}

// 返回
var finalDisplay = translatedText;
if (debugMode) {
finalDisplay = "【翻译结果】\n" + translatedText +
"\n\n【Debug 原始请求包】\n" + rawData +
"\n\n【解析参数】\n原文: " + sourceText +
"\n目标语言: " + langTo;
}
return createResponse(finalDisplay);
}

function createResponse(text) {
var out = {
"choices": [{
"message": {
"role": "assistant",
"content": text
},
"finish_reason": "stop"
}]
};
return ContentService.createTextOutput(JSON.stringify(out))
.setMimeType(ContentService.MimeType.JSON);
}

function doGet(e) {
return ContentService.createTextOutput("服务正常。请在使用 POST 请求。");
}

点击“部署 - 新建部署”,点击左边小齿轮 - web 应用,然后配置参数说明随便填,具有访问权限的用户改成选择 “所有人” (Anyone),最后点击底部的 “部署”。

部署成功会生成一串地址,我们复制这一串地址,保存待用。

在 Zotero 中使用

打开Zotero的设置 - 翻译 - 翻译服务,拉到最下面有三个自定义GPT选项,密钥不用填,点击配置,把刚才复制的一串地址粘贴到接口里面,模型和温度都不用设置,然后提示词按照下面输入:

1
${sourceText} ---> ${langTo}

这里 ${sourceText} 就是你要翻译的原文,${langTo} 是你要翻译成的语言,然后我们把流式输出关闭,点击保存。

最后还有一点就是把“向GPT/Claude/Gemini服务提供论文标题和摘要”这一设置项取消勾选,当然你要是后面改用其他大模型翻译的话,可以把这个选项再勾回去。

大陆访问

Apps Script 给到的原始链接是不能在大陆访问的,可以给它套一个 Cloudflare,步骤如下:

  1. 登录 Cloudflare 控制台,在左侧菜单栏点击 “Compute” -> “Workers 和 Pages”,点击 “创建应用程序” -> “从 Hello World! 开始”。给你的 Worker 起个新名字或者保持默认,点击 “部署”。
  2. 部署成功后,点击 “编辑代码”,将编辑器里的原有代码全部删掉,替换为下面这段,别忘了修改GOOGLE_URL变量的值为 Apps Script 部署地址。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    export default {
    async fetch(request, env, ctx) {
    const GOOGLE_URL = "这里填你的 Apps Script 部署地址";

    if (request.method === "OPTIONS") {
    return new Response(null, {
    headers: {
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
    "Access-Control-Allow-Headers": "*",
    },
    });
    }

    const cleanHeaders = new Headers();
    cleanHeaders.set("Content-Type", "application/json");
    let fetchConfig = {
    method: request.method,
    headers: cleanHeaders,
    redirect: "follow"
    };

    if (request.method === "POST") {
    fetchConfig.body = await request.clone().text();
    }

    try {
    const response = await fetch(GOOGLE_URL, fetchConfig);
    const resBody = await response.text();

    return new Response(resBody, {
    headers: {
    "Content-Type": "application/json",
    "Access-Control-Allow-Origin": "*",
    },
    });
    } catch (err) {
    return new Response(JSON.stringify({ error: "Worker Error: " + err.toString() }), {
    status: 500,
    headers: { "Access-Control-Allow-Origin": "*" }
    });
    }
    },
    };
  3. 返回管理页面,点击上方的 “设置” 选项卡,选择左侧的 “域和路由” (Domains & Routes),点击 “添加” -> “自定义域”,输入你的域名。
  4. 如果你的域名DNS解析也在 Cloudflare,那么 Cloudflare 会自动帮你配置好 DNS 解析和 SSL 证书。如果不在,则打开你的域名解析服务,添加一条CNAME记录,值就填前面 cloudflare 设置域和路由界面的 workers.dev 的值。

沉浸式翻译插件

沉浸式翻译插件是个非常好用的浏览器插件,有非常多的自定义项,我们也可以把这个翻译服务接入到这个插件里面。

这个插件的{{to}}参数比较特殊,需要做一下映射转换,代码需要替换为下面这个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

function doPost(e) {
try {
if (!e || !e.postData || !e.postData.contents) {
return createResponse("Error: No data received");
}
var payload = JSON.parse(e.postData.contents);
var fullContent = "";
if (payload.messages && payload.messages.length > 0) {
fullContent = payload.messages[payload.messages.length - 1].content;
} else if (payload.prompt) {
fullContent = payload.prompt;
} else {
fullContent = payload.text || "";
}
if (!fullContent) return createResponse("Error: Content is empty");
var sourceText = fullContent;
var targetLang = "zh-CN";
if (fullContent.indexOf("--->") !== -1) {
var parts = fullContent.split("--->");
var langPart = parts.pop().trim();
if (langPart && langPart.indexOf("{") === -1 && langPart.indexOf("}") === -1) {
targetLang = langPart;
sourceText = parts.join("--->").trim();
}
}
var cleanLang = targetLang.toLowerCase().replace(" language", "").trim();
var langMap = {
'simplified chinese': 'zh-CN',
'traditional chinese (taiwan)': 'zh-TW',
'traditional chinese': 'zh-TW',
'cantonese': 'yue',
'english': 'en',
'japanese': 'ja',
'korean': 'ko',
'spanish': 'es',
'german': 'de',
'french': 'fr',
'portuguese': 'pt',
'russian': 'ru',
'arabic': 'ar',
'italian': 'it',
'thai': 'th',
'mongolian': 'mn',
'uyghur': 'ug',
'zh': 'zh-CN',
'cn': 'zh-CN',
'jp': 'ja',
'kr': 'ko'
};
targetLang = langMap[cleanLang] || targetLang;
var translatedText = LanguageApp.translate(sourceText, '', targetLang);
return createResponse(translatedText);
} catch (err) {
return createResponse("GAS Crash: " + err.toString() + " (Target: " + targetLang + ")");
}
}
function createResponse(text) {
var response = {
"choices": [{
"message": { "role": "assistant", "content": text },
"finish_reason": "stop"
}]
};
return ContentService.createTextOutput(JSON.stringify(response))
.setMimeType(ContentService.MimeType.JSON);
}
function doGet(e) {
return ContentService.createTextOutput("API 服务已就绪。")
.setMimeType(ContentService.MimeType.TEXT);
}

提示词直接换成{{text}}--->{{to}}即可。