@sns-parse/ext-translate 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @sns-parse/ext-translate:推文翻译(Google gtx + MyMemory 兜底)。
3
+ */
4
+ import { translateText } from './translate';
5
+ export { translateText, shouldSkipTranslate, langName } from './translate';
6
+ export type { TranslateResult } from './translate';
7
+ /** 组装为 core 扩展片段 */
8
+ export declare function translateExtension(): {
9
+ translate: typeof translateText;
10
+ };
package/lib/index.js ADDED
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.langName = exports.shouldSkipTranslate = exports.translateText = void 0;
4
+ exports.translateExtension = translateExtension;
5
+ /**
6
+ * @sns-parse/ext-translate:推文翻译(Google gtx + MyMemory 兜底)。
7
+ */
8
+ const translate_1 = require("./translate");
9
+ var translate_2 = require("./translate");
10
+ Object.defineProperty(exports, "translateText", { enumerable: true, get: function () { return translate_2.translateText; } });
11
+ Object.defineProperty(exports, "shouldSkipTranslate", { enumerable: true, get: function () { return translate_2.shouldSkipTranslate; } });
12
+ Object.defineProperty(exports, "langName", { enumerable: true, get: function () { return translate_2.langName; } });
13
+ /** 组装为 core 扩展片段 */
14
+ function translateExtension() {
15
+ return { translate: translate_1.translateText };
16
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * 推文翻译:Google gtx 免费端点(无 Key、GET-only)。
3
+ *
4
+ * X 网页端的 Grok 翻译走私有通道(translations/show.json 已 404,GraphQL
5
+ * 仅返回 is_translatable 标志),无公开端点可调,故用通用翻译源。
6
+ * 译文存入 ParsedData.translation,经 ${翻译} 模板变量展示,空值行自动隐藏。
7
+ */
8
+ import type { ParserRuntimeLike } from '@sns-parse/core';
9
+ /** 判断是否需要跳过翻译(目标语系与推文语种相同 / 无语言内容) */
10
+ export declare function shouldSkipTranslate(tweetLang: string | undefined, target: string): boolean;
11
+ export declare function langName(code?: string): string;
12
+ export interface TranslateResult {
13
+ text: string;
14
+ provider: 'Google' | 'MyMemory';
15
+ }
16
+ /** 翻译文本;失败返回 null(调用方保持无译文继续发送)。gtx 优先,MyMemory 兜底 */
17
+ export declare function translateText(rt: ParserRuntimeLike, text: string, target: string, sourceLang?: string): Promise<TranslateResult | null>;
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.shouldSkipTranslate = shouldSkipTranslate;
4
+ exports.langName = langName;
5
+ exports.translateText = translateText;
6
+ const core_1 = require("@sns-parse/core");
7
+ /** 判断是否需要跳过翻译(目标语系与推文语种相同 / 无语言内容) */
8
+ function shouldSkipTranslate(tweetLang, target) {
9
+ if (!tweetLang)
10
+ return false;
11
+ const l = tweetLang.toLowerCase();
12
+ if (l === 'zxx' || l === 'und')
13
+ return l === 'zxx'; // zxx=无语言内容;und=未定则仍尝试
14
+ const t = target.toLowerCase();
15
+ if (t === 'zh')
16
+ return l === 'zh' || l.startsWith('zh-');
17
+ if (t === 'zh-tw')
18
+ return l === 'zh-tw' || l === 'zh-hant' || (l.startsWith('zh') && l !== 'zh-cn');
19
+ if (t === 'en')
20
+ return l === 'en';
21
+ return l === t;
22
+ }
23
+ /** 常见语种代码 → 中文名(未知代码原样展示) */
24
+ const LANG_NAMES = {
25
+ fr: '法语', ja: '日语', ko: '韩语', en: '英语', zh: '中文', es: '西班牙语', pt: '葡萄牙语',
26
+ de: '德语', ru: '俄语', ar: '阿拉伯语', it: '意大利语', th: '泰语', vi: '越南语',
27
+ id: '印尼语', tr: '土耳其语', nl: '荷兰语', pl: '波兰语', hi: '印地语', uk: '乌克兰语',
28
+ sv: '瑞典语', no: '挪威语', fi: '芬兰语', da: '丹麦语', cs: '捷克语', ro: '罗马尼亚语',
29
+ hu: '匈牙利语', el: '希腊语', he: '希伯来语', fa: '波斯语', ur: '乌尔都语', ms: '马来语',
30
+ tl: '菲律宾语', ca: '加泰罗尼亚语', bg: '保加利亚语', hr: '克罗地亚语', sr: '塞尔维亚语',
31
+ };
32
+ function langName(code) {
33
+ if (!code)
34
+ return '';
35
+ const base = code.toLowerCase().split('-')[0];
36
+ if (base === 'und')
37
+ return '';
38
+ return LANG_NAMES[base] || code;
39
+ }
40
+ /** 翻译文本;失败返回 null(调用方保持无译文继续发送)。gtx 优先,MyMemory 兜底 */
41
+ async function translateText(rt, text, target, sourceLang) {
42
+ if (!text)
43
+ return null;
44
+ const out = await viaGtx(rt, text, target);
45
+ if (out)
46
+ return { text: out, provider: 'Google' };
47
+ const backup = await viaMyMemory(rt, text, target, sourceLang);
48
+ if (backup)
49
+ return { text: backup, provider: 'MyMemory' };
50
+ return null;
51
+ }
52
+ /** Google gtx 免费端点(无 Key;sl=auto 自动检测源语种) */
53
+ async function viaGtx(rt, text, target) {
54
+ const url = 'https://translate.googleapis.com/translate_a/single' +
55
+ `?client=gtx&sl=auto&dt=t&tl=${encodeURIComponent(target)}` +
56
+ `&q=${encodeURIComponent(text.slice(0, 2000))}`;
57
+ try {
58
+ const res = await rt.http.get(url, {
59
+ timeout: 15000,
60
+ headers: { 'User-Agent': rt.config.userAgent },
61
+ });
62
+ const data = res.data;
63
+ // gtx 响应形如 [[["译文","原文",null,null,10]],null,"fr",...]
64
+ if (Array.isArray(data?.[0])) {
65
+ const out = data[0].map((seg) => (typeof seg?.[0] === 'string' ? seg[0] : '')).join('').trim();
66
+ if (out)
67
+ return out;
68
+ }
69
+ (0, core_1.debugLog)('gtx 翻译响应结构异常,尝试备用通道');
70
+ return null;
71
+ }
72
+ catch (e) {
73
+ (0, core_1.debugLog)(`gtx 翻译失败:${e?.message || e}`);
74
+ return null;
75
+ }
76
+ }
77
+ /** MyMemory 备用(免费无 Key;需已知源语种,不支持 auto) */
78
+ async function viaMyMemory(rt, text, target, sourceLang) {
79
+ const sl = normalizeLang(sourceLang);
80
+ if (!sl)
81
+ return null;
82
+ const tl = target.toLowerCase();
83
+ const url = 'https://api.mymemory.translated.net/get' +
84
+ `?q=${encodeURIComponent(text.slice(0, 500))}&langpair=${encodeURIComponent(sl + '|' + tl)}`;
85
+ try {
86
+ const res = await rt.http.get(url, { timeout: 15000, headers: { 'User-Agent': rt.config.userAgent } });
87
+ const out = res.data?.responseData?.translatedText;
88
+ if (typeof out === 'string' && out.trim() && !/^MYMEMORY WARNING/i.test(out))
89
+ return out.trim();
90
+ (0, core_1.debugLog)('MyMemory 翻译响应异常,放弃译文');
91
+ return null;
92
+ }
93
+ catch (e) {
94
+ core_1.logger.info(`推文翻译失败(跳过译文继续发送):${e?.message || e}`);
95
+ return null;
96
+ }
97
+ }
98
+ function normalizeLang(lang) {
99
+ if (!lang)
100
+ return null;
101
+ const l = lang.toLowerCase();
102
+ if (l === 'und' || l === 'zxx')
103
+ return null;
104
+ if (l.startsWith('zh'))
105
+ return 'zh-CN';
106
+ return l.split('-')[0];
107
+ }
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@sns-parse/ext-translate",
3
+ "version": "0.1.0-alpha.1+upstream.1.6.7",
4
+ "description": "sns-parse 扩展:translate",
5
+ "main": "lib/index.js",
6
+ "types": "lib/index.d.ts",
7
+ "files": [
8
+ "lib"
9
+ ],
10
+ "license": "MIT",
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "clean": "rimraf lib",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "dependencies": {
17
+ "@sns-parse/core": "^0.1.0",
18
+ "axios": "^1.16.1"
19
+ },
20
+ "devDependencies": {
21
+ "@types/node": "^20.11.0",
22
+ "rimraf": "^5.0.5",
23
+ "typescript": "^5.3.3"
24
+ },
25
+ "publishConfig": {
26
+ "access": "public",
27
+ "registry": "https://registry.npmjs.org"
28
+ },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/sns-parse/extensions.git",
32
+ "directory": "packages/ext-translate"
33
+ },
34
+ "engines": {
35
+ "node": ">=16.0.0"
36
+ }
37
+ }