Skip to content

Commit 5fbd604

Browse files
committed
fix(dicts): fix youdaotrans and caiyun translation engines
- youdaotrans: use free demo API, no API key required - caiyun: require user API token (website redesigned) - youdao: limit to short text (max: 5 words) - reorder: youdaotrans before caiyun - pdf.js: fix Windows compatibility (use curl) - README: add Node.js 17+ build instructions
1 parent ffb478c commit 5fbd604

File tree

11 files changed

+470
-127
lines changed

11 files changed

+470
-127
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,4 @@ test/**/response
9797

9898

9999
**/.DS_Store
100+
scripts/pdfjs.zip

README-zh.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,21 @@ yarn build
4545

4646
`build/` 目录下可查看针对各个浏览器打包好的扩展包。
4747

48+
### Node.js 17+ 用户注意
49+
50+
如果使用 Node.js 17 或更高版本,构建时可能遇到 OpenSSL 错误。请在构建前设置环境变量:
51+
52+
```bash
53+
# Linux/macOS
54+
export NODE_OPTIONS=--openssl-legacy-provider
55+
56+
# Windows (cmd)
57+
set NODE_OPTIONS=--openssl-legacy-provider
58+
59+
# Windows (PowerShell)
60+
$env:NODE_OPTIONS="--openssl-legacy-provider"
61+
```
62+
4863
## 开发
4964

5065
[项目贡献指南](./CONTRIBUTING-zh.md)

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,21 @@ yarn build
4949

5050
Artifacts can be found in `build/`.
5151

52+
### Note for Node.js 17+
53+
54+
If you are using Node.js 17 or later, you may encounter an OpenSSL error. Set this environment variable before building:
55+
56+
```bash
57+
# Linux/macOS
58+
export NODE_OPTIONS=--openssl-legacy-provider
59+
60+
# Windows (cmd)
61+
set NODE_OPTIONS=--openssl-legacy-provider
62+
63+
# Windows (PowerShell)
64+
$env:NODE_OPTIONS="--openssl-legacy-provider"
65+
```
66+
5267
## Development
5368

5469
See the [contributing guide](./CONTRIBUTING.md).

scripts/pdf.js

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ if (!shell.which('git')) {
1212
}
1313

1414
const cacheDir = 'pdf'
15-
const repoRoot = 'pdf'
15+
let repoRoot = 'pdf'
1616
const publicPDFRoot = path.join(__dirname, '../assets/pdf')
1717
const pdfFiles = [
1818
'build/pdf.js',
@@ -27,22 +27,38 @@ const files = [...pdfFiles, ...pdfDirs]
2727

2828
shell.cd(path.resolve(__dirname))
2929

30-
shell.rm('-rf', cacheDir)
30+
const distRoot = getPdfDistRoot()
31+
const useLocalDist =
32+
!!distRoot && fs.existsSync(path.join(distRoot, 'web/viewer.js'))
3133

32-
exec(
33-
`wget https://github.com/mozilla/pdf.js/releases/download/v2.16.105/pdfjs-2.16.105-dist.zip -O pdfjs.tar.gz &&
34-
mkdir -p ${cacheDir} &&
35-
tar -xzvf pdfjs.tar.gz -C ${cacheDir}`,
36-
'Error: download failed'
37-
)
34+
if (useLocalDist) {
35+
shell.echo('Using pdfjs-dist from node_modules.')
36+
repoRoot = distRoot
37+
} else {
38+
shell.rm('-rf', cacheDir)
39+
40+
shell.mkdir('-p', cacheDir)
41+
42+
const zipName = 'pdfjs.zip'
43+
const zipPath = path.join(__dirname, zipName)
44+
45+
const downloadCmd =
46+
`curl -L --retry 3 --retry-delay 2 --max-time 300 -k https://github.com/mozilla/pdf.js/releases/download/v2.16.105/pdfjs-2.16.105-dist.zip -o "${zipPath}"` +
47+
` || curl -L --retry 3 --retry-delay 2 --max-time 300 -k https://ghproxy.com/https://github.com/mozilla/pdf.js/releases/download/v2.16.105/pdfjs-2.16.105-dist.zip -o "${zipPath}"` +
48+
` || curl -L --retry 3 --retry-delay 2 --max-time 300 -k https://download.fastgit.org/mozilla/pdf.js/releases/download/v2.16.105/pdfjs-2.16.105-dist.zip -o "${zipPath}"`
3849

39-
shell.cd('./' + cacheDir)
50+
exec(downloadCmd, 'Error: download failed')
51+
52+
exec(`tar -xf "${zipPath}" -C "${cacheDir}"`, 'Error: download failed')
53+
54+
repoRoot = path.join(__dirname, cacheDir)
55+
}
4056

4157
startUpgrade()
4258

4359
async function startUpgrade() {
4460
shell.echo('\nChecking files.')
45-
await Promise.all(files.map(p => exists(path.join(__dirname, repoRoot, p))))
61+
await Promise.all(files.map(p => exists(resolveRepoPath(p))))
4662

4763
shell.echo('\nModifying files.')
4864
await Promise.all([modifyViewrJS(), modifyViewerHTML()])
@@ -62,7 +78,7 @@ async function startUpgrade() {
6278
}
6379

6480
async function modifyViewrJS() {
65-
const viewerPath = path.join(__dirname, repoRoot, 'web/viewer.js')
81+
const viewerPath = resolveRepoPath('web/viewer.js')
6682
let file = await fs.readFile(viewerPath, 'utf8')
6783

6884
file = '/* saladict */ window.__SALADICT_PDF_PAGE__ = true;\n' + file
@@ -97,7 +113,7 @@ async function modifyViewrJS() {
97113
}
98114

99115
async function modifyViewerHTML() {
100-
const viewerPath = path.join(__dirname, repoRoot, 'web/viewer.html')
116+
const viewerPath = resolveRepoPath('web/viewer.html')
101117
let file = await fs.readFile(viewerPath, 'utf8')
102118

103119
if (!file.includes(`</body>`)) {
@@ -126,6 +142,21 @@ function cleanInit() {
126142
})
127143
}
128144

145+
function getPdfDistRoot() {
146+
try {
147+
return path.dirname(require.resolve('pdfjs-dist/package.json'))
148+
} catch (e) {
149+
return null
150+
}
151+
}
152+
153+
function resolveRepoPath(subPath) {
154+
if (path.isAbsolute(repoRoot)) {
155+
return path.join(repoRoot, subPath)
156+
}
157+
return path.join(__dirname, repoRoot, subPath)
158+
}
159+
129160
async function exists(path) {
130161
try {
131162
await fs.access(path)
@@ -152,26 +183,26 @@ async function cloneFiles() {
152183
for (const pdfFile of pdfFiles) {
153184
const targetPath = path.join(publicPDFRoot, pdfFile)
154185
await fs.ensureFile(targetPath)
155-
await fs.copy(path.join(__dirname, repoRoot, pdfFile), targetPath)
186+
await fs.copy(resolveRepoPath(pdfFile), targetPath)
156187
}
157188

158189
const restPdfDirs = pdfDirs.filter(name => name !== 'web/locale')
159190

160191
for (const pdfDir of restPdfDirs) {
161192
const targetPath = path.join(publicPDFRoot, pdfDir)
162193
await fs.ensureDir(targetPath)
163-
await fs.copy(path.join(__dirname, repoRoot, pdfDir), targetPath)
194+
await fs.copy(resolveRepoPath(pdfDir), targetPath)
164195
}
165196

166197
// copy locale.properties
167198
await fs.ensureDir(path.join(publicPDFRoot, 'web/locale'))
168199
await fs.copy(
169-
path.join(__dirname, repoRoot, 'web/locale/locale.properties'),
200+
resolveRepoPath('web/locale/locale.properties'),
170201
path.join(publicPDFRoot, 'web/locale/locale.properties')
171202
)
172203

173204
const locales = (
174-
await fs.readdir(path.join(__dirname, repoRoot, 'web/locale'))
205+
await fs.readdir(resolveRepoPath('web/locale'))
175206
).filter(
176207
name =>
177208
name.startsWith('en') ||
@@ -182,9 +213,6 @@ async function cloneFiles() {
182213
for (const locale of locales) {
183214
const targetPath = path.join(publicPDFRoot, 'web/locale', locale)
184215
await fs.ensureDir(targetPath)
185-
await fs.copy(
186-
path.join(__dirname, repoRoot, 'web/locale', locale),
187-
targetPath
188-
)
216+
await fs.copy(resolveRepoPath(path.join('web/locale', locale)), targetPath)
189217
}
190218
}

src/app-config/profiles.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ export function _getDefaultProfile(id?: string) {
4242
'youdao',
4343
'urban',
4444
'vocabulary',
45-
'caiyun',
4645
'youdaotrans',
46+
'caiyun',
4747
'zdic',
4848
'guoyu',
4949
'liangan',
@@ -203,8 +203,8 @@ export function translation(): ProfileStorage {
203203
'google',
204204
'tencent',
205205
'baidu',
206-
'caiyun',
207206
'youdaotrans',
207+
'caiyun',
208208
'zdic',
209209
'guoyu',
210210
'liangan'

src/components/MachineTrans/engine.ts

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { DictID, AppConfig } from '@/app-config'
22
import { Language } from '@opentranslate/languages'
3-
import { Translator } from '@opentranslate/translator'
3+
import { Translator, TranslateResult } from '@opentranslate/translator'
44
import { DictItem, SelectOptions } from '@/app-config/dicts'
55
import { isContainJapanese, isContainKorean } from '@/_helpers/lang-check'
66
import { DictSearchResult } from '../dictionaries/helpers'
@@ -257,3 +257,91 @@ export function machineResult<ID extends DictID>(
257257
catalog
258258
}
259259
}
260+
261+
export const MACHINE_TRANSLATE_CHAR_LIMIT = 4500
262+
263+
function chunkText(text: string, limit = MACHINE_TRANSLATE_CHAR_LIMIT) {
264+
const chunks: string[] = []
265+
let current = ''
266+
267+
const pushCurrent = () => {
268+
if (current) {
269+
chunks.push(current)
270+
current = ''
271+
}
272+
}
273+
274+
for (const raw of text.split(/\n+/)) {
275+
const part = raw.trim()
276+
if (!part) {
277+
continue
278+
}
279+
280+
if (part.length > limit) {
281+
pushCurrent()
282+
for (let i = 0; i < part.length; i += limit) {
283+
chunks.push(part.slice(i, i + limit))
284+
}
285+
continue
286+
}
287+
288+
const candidate = current ? `${current}\n${part}` : part
289+
if (candidate.length > limit) {
290+
pushCurrent()
291+
current = part
292+
} else {
293+
current = candidate
294+
}
295+
}
296+
297+
pushCurrent()
298+
299+
return chunks.length ? chunks : ['']
300+
}
301+
302+
export async function translateInChunks(
303+
translator: Translator,
304+
text: string,
305+
sl: Language,
306+
tl: Language,
307+
config: Record<string, unknown> | undefined,
308+
limit = MACHINE_TRANSLATE_CHAR_LIMIT
309+
): Promise<TranslateResult> {
310+
const segments = chunkText(text, limit)
311+
312+
const aggregated: TranslateResult = {
313+
from: sl,
314+
to: tl,
315+
origin: { paragraphs: [] },
316+
trans: { paragraphs: [] }
317+
}
318+
319+
let currentSl = sl
320+
let currentTl = tl
321+
322+
for (const segment of segments) {
323+
const res = await translator.translate(
324+
segment,
325+
currentSl,
326+
currentTl,
327+
config
328+
)
329+
330+
aggregated.from = res.from
331+
aggregated.to = res.to
332+
aggregated.origin.paragraphs.push(...res.origin.paragraphs)
333+
aggregated.trans.paragraphs.push(...res.trans.paragraphs)
334+
335+
if (res.origin.tts && !aggregated.origin.tts) {
336+
aggregated.origin.tts = res.origin.tts
337+
}
338+
if (res.trans.tts && !aggregated.trans.tts) {
339+
aggregated.trans.tts = res.trans.tts
340+
}
341+
342+
currentSl = res.from as Language
343+
currentTl = res.to as Language
344+
}
345+
346+
return aggregated
347+
}

src/components/dictionaries/baidu/engine.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import {
55
MachineTranslateResult,
66
MachineTranslatePayload,
77
getMTArgs,
8-
machineResult
8+
machineResult,
9+
translateInChunks
910
} from '@/components/MachineTrans/engine'
1011
import { BaiduLanguage } from './config'
1112

@@ -56,8 +57,31 @@ export const search: SearchFunction<
5657
const key = config.dictAuth.baidu.key
5758
const translatorConfig = appid && key ? { appid, key } : undefined
5859

60+
if (!translatorConfig) {
61+
return machineResult(
62+
{
63+
result: {
64+
id: 'baidu',
65+
slInitial: 'hide',
66+
sl,
67+
tl,
68+
searchText: { paragraphs: [''] },
69+
trans: { paragraphs: [''] },
70+
requireCredential: true
71+
}
72+
},
73+
translator.getSupportLanguages()
74+
)
75+
}
76+
5977
try {
60-
const result = await translator.translate(text, sl, tl, translatorConfig)
78+
const result = await translateInChunks(
79+
translator,
80+
text,
81+
sl,
82+
tl,
83+
translatorConfig
84+
)
6185
return machineResult(
6286
{
6387
result: {

0 commit comments

Comments
 (0)