Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
T
translation-server
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
qinmingyuan
translation-server
Commits
1324bcb1
Commit
1324bcb1
authored
Jan 15, 2025
by
mingyard
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat:翻译狗相关接口
parent
133fdb1d
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
383 additions
and
9 deletions
+383
-9
launch.json
.vscode/launch.json
+16
-0
constants.ts
src/common/utils/constants.ts
+68
-0
service.ts
src/config/service.ts
+2
-0
translateImageReq.dto.ts
src/controller/translate/dto/req/translateImageReq.dto.ts
+29
-0
translateProgressReq.dto.ts
src/controller/translate/dto/req/translateProgressReq.dto.ts
+15
-0
translate.controller.ts
src/controller/translate/translate.controller.ts
+120
-5
translate.service.ts
src/controller/translate/translate.service.ts
+133
-4
No files found.
.vscode/launch.json
0 → 100644
View file @
1324bcb1
{
//
使用
IntelliSense
了解相关属性。
//
悬停以查看现有属性的描述。
//
欲了解更多信息,请访问
:
https
:
//go.microsoft.com/fwlink/?linkid=
830387
"version"
:
"0.2.0"
,
"configurations"
:
[
{
"type"
:
"node-terminal"
,
"name"
:
"运行脚本: start"
,
"request"
:
"launch"
,
"command"
:
"yarn run start"
,
"cwd"
:
"${workspaceFolder}"
}
]
}
\ No newline at end of file
src/common/utils/constants.ts
View file @
1324bcb1
...
...
@@ -101,3 +101,71 @@ export enum SupportLanguages {
UK
=
'UK'
,
ZH
=
'ZH'
,
}
// 翻译狗支持的语言
export
const
FAN_YI_GOU_SOURCE_LANGUAGES
=
[
{
key
:
'pl'
,
language
:
'Polish'
,
name
:
'波兰'
},
{
key
:
'dan'
,
language
:
'Danish'
,
name
:
'丹麦语'
},
{
key
:
'de'
,
language
:
'German'
,
name
:
'德语'
},
{
key
:
'ru'
,
language
:
'Russian'
,
name
:
'俄语'
},
{
key
:
'fra'
,
language
:
'French'
,
name
:
'法语'
},
{
key
:
'kor'
,
language
:
'Korean'
,
name
:
'韩语'
},
{
key
:
'nl'
,
language
:
'Dutch'
,
name
:
'荷兰语'
},
{
key
:
'rom'
,
language
:
'Romanian'
,
name
:
'罗马尼亚语'
},
{
key
:
'ms'
,
language
:
'Malay'
,
name
:
'马来语'
},
{
key
:
'pt'
,
language
:
'Portuguese'
,
name
:
'葡萄牙语'
},
{
key
:
'jp'
,
language
:
'Japanese'
,
name
:
'日语'
},
{
key
:
'swe'
,
language
:
'Swedish'
,
name
:
'瑞典语'
},
{
key
:
'tr'
,
language
:
'Turkish'
,
name
:
'土耳其语'
},
{
key
:
'spa'
,
language
:
'Spanish'
,
name
:
'西班牙语'
},
{
key
:
'el'
,
language
:
'Greek'
,
name
:
'希腊语'
},
{
key
:
'hu'
,
language
:
'Hungarian'
,
name
:
'匈牙利语'
},
{
key
:
'it'
,
language
:
'Italian'
,
name
:
'意大利语'
},
{
key
:
'id'
,
language
:
'Indonesian'
,
name
:
'印尼语'
},
{
key
:
'en'
,
language
:
'English'
,
name
:
'英语'
},
{
key
:
'zh'
,
language
:
'Chinese'
,
name
:
'中文'
},
];
// 翻译狗支持的目标语言
export
const
FAN_YI_GOU_TARGET_LANGUAGES
=
[
{
key
:
'kor'
,
language
:
'Korean'
,
name
:
'韩语'
},
{
key
:
'jp'
,
language
:
'Japanese'
,
name
:
'日语'
},
{
key
:
'th'
,
language
:
'Thai'
,
name
:
'泰语'
},
{
key
:
'el'
,
language
:
'Greek'
,
name
:
'希腊语'
},
{
key
:
'en'
,
language
:
'English'
,
name
:
'英语'
},
{
key
:
'zh'
,
language
:
'Chinese'
,
name
:
'中文'
},
];
// 翻译狗支持的语言
export
const
FanYiGouSourceLanguages
=
{
PL
:
'pl'
,
DAN
:
'dan'
,
DE
:
'de'
,
RU
:
'ru'
,
FRA
:
'fra'
,
KOR
:
'kor'
,
NL
:
'nl'
,
ROM
:
'rom'
,
MS
:
'ms'
,
PT
:
'pt'
,
JP
:
'jp'
,
SWE
:
'swe'
,
TR
:
'tr'
,
SPA
:
'spa'
,
EL
:
'el'
,
HU
:
'hu'
,
IT
:
'it'
,
ID
:
'id'
,
EN
:
'en'
,
ZH
:
'zh'
,
};
// 翻译狗支持的目标语言
export
const
FanYiGouTargetLanguages
=
{
KOR
:
'kor'
,
JP
:
'jp'
,
TH
:
'th'
,
EL
:
'el'
,
EN
:
'en'
,
ZH
:
'zh'
,
};
src/config/service.ts
View file @
1324bcb1
...
...
@@ -7,6 +7,7 @@ export interface ServiceConfig {
fanYiGou
:
{
endpoint
:
string
;
secret
:
string
;
appId
:
string
;
};
}
...
...
@@ -17,5 +18,6 @@ export const service: ServiceConfig = {
fanYiGou
:
{
endpoint
:
env
.
FAN_YI_GOU_ENDPOINT
??
'https://www.fanyigou.com'
,
secret
:
env
.
FAN_YI_GOU_SECRET
??
''
,
appId
:
env
.
FAN_YI_GOU_APP_ID
??
''
,
},
};
src/controller/translate/dto/req/translateImageReq.dto.ts
0 → 100644
View file @
1324bcb1
import
{
FanYiGouSourceLanguages
,
FanYiGouTargetLanguages
,
}
from
'@/common/utils/constants'
;
import
{
ApiProperty
}
from
'@nestjs/swagger'
;
import
{
Expose
}
from
'class-transformer'
;
import
{
IsEnum
,
IsNotEmpty
}
from
'class-validator'
;
export
class
TranslateImageReqDto
{
@
ApiProperty
({
description
:
'目标语言'
,
enum
:
FanYiGouTargetLanguages
,
example
:
FanYiGouTargetLanguages
.
EN
,
})
@
Expose
()
@
IsNotEmpty
()
@
IsEnum
(
FanYiGouTargetLanguages
)
target
:
string
;
@
ApiProperty
({
description
:
'源语言'
,
enum
:
FanYiGouSourceLanguages
,
example
:
FanYiGouSourceLanguages
.
ZH
,
})
@
Expose
()
@
IsNotEmpty
()
@
IsEnum
(
FanYiGouSourceLanguages
)
source
:
string
;
}
src/controller/translate/dto/req/translateProgressReq.dto.ts
0 → 100644
View file @
1324bcb1
import
{
ApiProperty
}
from
'@nestjs/swagger'
;
import
{
Expose
}
from
'class-transformer'
;
import
{
IsNotEmpty
,
IsString
}
from
'class-validator'
;
export
class
TranslateProgressReqDto
{
@
ApiProperty
({
description
:
'任务ID'
,
type
:
String
,
example
:
123456
,
})
@
Expose
()
@
IsNotEmpty
()
@
IsString
()
taskId
:
string
;
}
src/controller/translate/translate.controller.ts
View file @
1324bcb1
import
{
Body
,
Controller
,
Get
,
Post
}
from
'@nestjs/common'
;
import
{
Body
,
Controller
,
Get
,
Post
,
Query
,
UploadedFile
,
UseInterceptors
,
}
from
'@nestjs/common'
;
import
{
TranslateService
}
from
'./translate.service'
;
import
{
Auth
}
from
'@/common/decorators/auth.decorator'
;
import
{
ApiTags
,
ApiOperation
,
ApiResponse
}
from
'@nestjs/swagger'
;
import
{
TranslateReqDto
}
from
'./dto/req/translateReq.dto'
;
import
{
TranslateResDto
}
from
'./dto/res/translateRes.dto'
;
import
{
FileInterceptor
}
from
'@nestjs/platform-express'
;
import
{
TranslateImageReqDto
}
from
'./dto/req/translateImageReq.dto'
;
import
{
TranslateProgressReqDto
}
from
'./dto/req/translateProgressReq.dto'
;
import
{
ApiResponseInterceptor
}
from
'@/common/interceptor/api.response.interceptor'
;
@
ApiTags
(
'translate'
)
@
UseInterceptors
(
ApiResponseInterceptor
)
@
Controller
(
'translate'
)
export
class
TranslateController
{
constructor
(
private
readonly
translateService
:
TranslateService
)
{}
...
...
@@ -17,9 +30,13 @@ export class TranslateController {
status
:
200
,
description
:
'成功返回支持的语言列表'
,
example
:
{
code
:
200
,
message
:
'success'
,
data
:
{
source
:
[{
key
:
'AR'
,
language
:
'Arabic'
,
name
:
'阿拉伯语'
}],
target
:
[{
key
:
'AR'
,
language
:
'Arabic'
,
name
:
'阿拉伯语'
}],
},
},
})
@
Auth
()
async
getLanguages
():
Promise
<
{
source
:
object
[];
target
:
object
[]
}
>
{
...
...
@@ -33,8 +50,12 @@ export class TranslateController {
status
:
200
,
description
:
'成功返回翻译结果'
,
example
:
{
source
:
'EN'
,
text
:
'要翻译的文本'
,
code
:
200
,
message
:
'success'
,
data
:
{
source
:
'ZH'
,
text
:
'weekly'
,
},
},
})
@
Auth
()
...
...
@@ -43,4 +64,98 @@ export class TranslateController {
):
Promise
<
TranslateResDto
>
{
return
await
this
.
translateService
.
translateText
(
translateDto
);
}
// 翻译图片支持的语言
@
Get
(
'image/languages'
)
@
ApiOperation
({
summary
:
'获取翻译图片支持的语言'
})
@
ApiResponse
({
status
:
200
,
description
:
'成功返回支持的语言列表'
,
example
:
{
code
:
200
,
message
:
'success'
,
data
:
{
source
:
[{
key
:
'en'
,
language
:
'English'
,
name
:
'英语'
}],
target
:
[{
key
:
'zh'
,
language
:
'Chinese'
,
name
:
'中文'
}],
},
},
})
@
Auth
()
async
getImageLanguages
():
Promise
<
{
source
:
object
[];
target
:
object
[];
}
>
{
return
await
this
.
translateService
.
getFanYiGouLanguages
();
}
// 翻译图片
@
Post
(
'image'
)
@
ApiOperation
({
summary
:
'翻译图片'
})
@
ApiResponse
({
status
:
200
,
description
:
'成功返回翻译结果'
,
example
:
{
code
:
200
,
message
:
'success'
,
data
:
{
taskId
:
709394
,
},
},
})
@
Auth
()
@
UseInterceptors
(
FileInterceptor
(
'file'
,
{
limits
:
{
fileSize
:
4
*
1024
*
1024
},
}),
)
async
translateImage
(
@
UploadedFile
()
file
,
@
Body
()
dto
:
TranslateImageReqDto
,
):
Promise
<
any
>
{
return
await
this
.
translateService
.
translateImage
(
file
,
dto
);
}
// 获取翻译进度
@
Get
(
'progress'
)
@
ApiOperation
({
summary
:
'获取翻译进度'
})
@
ApiResponse
({
status
:
200
,
description
:
'成功返回翻译进度'
,
example
:
{
code
:
200
,
message
:
'success'
,
data
:
{
tid
:
709412
,
createTime
:
'2025-01-15 16:54:08'
,
updateTime
:
''
,
status
:
1
,
title
:
'20250111-165032'
,
pageCount
:
1
,
percent
:
'0.00'
,
fromLan
:
'英语'
,
detectLan
:
''
,
toLan
:
'中文'
,
docType
:
'jpeg'
,
msg
:
'进行中'
,
originalCharCount
:
0
,
},
},
})
@
Auth
()
async
getProgress
(@
Query
()
dto
:
TranslateProgressReqDto
):
Promise
<
any
>
{
return
await
this
.
translateService
.
getProgress
(
dto
.
taskId
);
}
// 获取翻译结果
@
Get
(
'result'
)
@
ApiOperation
({
summary
:
'获取翻译结果'
})
@
ApiResponse
({
status
:
200
,
description
:
'成功返回翻译结果'
,
example
:
{},
})
@
Auth
()
async
getResult
(@
Query
()
dto
:
TranslateProgressReqDto
):
Promise
<
any
>
{
return
await
this
.
translateService
.
downloadImage
(
dto
.
taskId
);
}
}
src/controller/translate/translate.service.ts
View file @
1324bcb1
...
...
@@ -8,7 +8,9 @@ import { TranslateResDto } from './dto/res/translateRes.dto';
import
{
axiosPostRequest
}
from
'@/common/utils/requests/request'
;
import
{
config
}
from
'@/config'
;
import
{
BadRequestError
}
from
'@/common/exception/badRequest/BadRequestError'
;
import
crypto
from
'crypto'
;
import
*
as
crypto
from
'crypto'
;
import
*
as
FormData
from
'form-data'
;
import
{
TranslateImageReqDto
}
from
'./dto/req/translateImageReq.dto'
;
@
Injectable
()
export
class
TranslateService
{
...
...
@@ -22,6 +24,17 @@ export class TranslateService {
};
}
// 获取翻译狗支持的语言
async
getFanYiGouLanguages
():
Promise
<
{
source
:
object
[];
target
:
object
[];
}
>
{
return
{
source
:
DEEPL_SOURCE_LANGUAGES
,
target
:
DEEPL_TARGET_LANGUAGES
,
};
}
// 翻译文本
async
translateText
(
translateDto
:
TranslateReqDto
):
Promise
<
TranslateResDto
>
{
console
.
log
(
'config.deepl'
,
config
.
deepl
);
...
...
@@ -52,21 +65,137 @@ export class TranslateService {
}
// 获取翻译狗token
async
generateToken
(
params
,
privateKey
)
{
generateToken
(
params
)
{
params
=
{
...
params
,
appid
:
config
.
service
.
fanYiGou
.
appId
,
privatekey
:
config
.
service
.
fanYiGou
.
secret
,
};
// 按照key=value的格式,并按照参数名ASCII字典序排序
const
sortedParams
=
Object
.
keys
(
params
)
.
sort
()
.
map
((
key
)
=>
`
${
key
}
=
${
params
[
key
]}
`
)
.
join
(
'&'
);
const
stringToHash
=
`
${
sortedParams
}
&privatekey=
${
privateKey
}
`
;
// 生成MD5哈希并转换为大写
const
token
=
crypto
.
createHash
(
'md5'
)
.
update
(
s
tringToHash
)
.
update
(
s
ortedParams
)
.
digest
(
'hex'
)
.
toUpperCase
();
return
token
;
}
getFileMD5
(
fileBuffer
:
Buffer
):
string
{
return
crypto
.
createHash
(
'md5'
).
update
(
fileBuffer
).
digest
(
'hex'
);
}
async
translateImage
(
file
,
dto
:
TranslateImageReqDto
):
Promise
<
any
>
{
const
params
=
{
nonce_str
:
crypto
.
randomUUID
(),
from
:
dto
.
source
,
to
:
dto
.
target
,
md5
:
this
.
getFileMD5
(
file
.
buffer
),
};
const
token
=
this
.
generateToken
(
params
);
// 构建 FormData 数据
const
formData
=
new
FormData
();
formData
.
append
(
'file'
,
file
.
buffer
,
{
filename
:
file
.
originalname
,
contentType
:
file
.
mimetype
,
});
formData
.
append
(
'appid'
,
config
.
service
.
fanYiGou
.
appId
);
formData
.
append
(
'md5'
,
params
.
md5
);
formData
.
append
(
'nonce_str'
,
params
.
nonce_str
);
formData
.
append
(
'from'
,
params
.
from
);
formData
.
append
(
'to'
,
params
.
to
);
formData
.
append
(
'token'
,
token
);
const
result
=
await
axiosPostRequest
(
`
${
config
.
service
.
fanYiGou
.
endpoint
}
/TranslateApi/api/image/uploadTranslateImage`
,
formData
,
{
headers
:
{
...
formData
.
getHeaders
(),
},
},
);
if
(
!
result
?.
data
)
{
throw
BadRequestError
.
default
(
'翻译失败'
);
}
return
result
?.
data
?.
data
;
}
// 获取翻译进度
async
getProgress
(
taskId
:
string
):
Promise
<
any
>
{
const
params
=
{
nonce_str
:
crypto
.
randomUUID
(),
tid
:
taskId
,
};
const
token
=
this
.
generateToken
(
params
);
// 构建 FormData 数据
const
formData
=
new
FormData
();
formData
.
append
(
'appid'
,
config
.
service
.
fanYiGou
.
appId
);
formData
.
append
(
'nonce_str'
,
params
.
nonce_str
);
formData
.
append
(
'tid'
,
params
.
tid
);
formData
.
append
(
'token'
,
token
);
const
result
=
await
axiosPostRequest
(
`
${
config
.
service
.
fanYiGou
.
endpoint
}
/TranslateApi/api/image/queryImageTransProgress`
,
formData
,
{
headers
:
{
...
formData
.
getHeaders
(),
},
},
);
if
(
!
result
?.
data
)
{
throw
BadRequestError
.
default
(
'获取翻译进度失败'
);
}
return
result
?.
data
.
data
;
}
// 下载翻译图片
async
downloadImage
(
taskId
:
string
):
Promise
<
any
>
{
const
params
=
{
nonce_str
:
crypto
.
randomUUID
(),
tid
:
taskId
,
};
const
token
=
this
.
generateToken
(
params
);
// 构建 FormData 数据
const
formData
=
new
FormData
();
formData
.
append
(
'appid'
,
config
.
service
.
fanYiGou
.
appId
);
formData
.
append
(
'nonce_str'
,
params
.
nonce_str
);
formData
.
append
(
'tid'
,
params
.
tid
);
formData
.
append
(
'token'
,
token
);
const
result
=
await
axiosPostRequest
(
`
${
config
.
service
.
fanYiGou
.
endpoint
}
/TranslateApi/api/image/downloadImage`
,
formData
,
{
headers
:
{
...
formData
.
getHeaders
(),
},
},
);
if
(
!
result
?.
data
)
{
throw
BadRequestError
.
default
(
'下载翻译图片失败'
);
}
return
result
?.
data
;
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment