diff --git a/api/index.js b/api/index.js index c0a9c881a..ff2a2a0e6 100644 --- a/api/index.js +++ b/api/index.js @@ -30,7 +30,6 @@ apiRouter.use('/customer-services', require('./customerService/api')) apiRouter.get('/debug/search', parseSearchingQ, (req, res) => { res.json({ q: req.q, query: req.query }) }) -apiRouter.use('/translate', require('./translate/api')) apiRouter.use('/quick-replies', require('./quick-reply/api')) const router = Router() diff --git a/api/translate/api.js b/api/translate/api.js deleted file mode 100644 index 58ed8ffa5..000000000 --- a/api/translate/api.js +++ /dev/null @@ -1,48 +0,0 @@ -const { Router } = require('express') -const { check } = require('express-validator') - -const { requireAuth, catchError, customerServiceOnly } = require('../middleware') -const { translate } = require('./utils') - -const router = Router().use(requireAuth) - -function getAcceptLanguages(acceptLanguage) { - const languages = acceptLanguage.split(',').map((lang) => { - const [locale, q = '1'] = lang.split(/;\s*q=/) - return { locale: locale.trim(), q: Number(q) } - }) - languages.sort((a, b) => b.q - a.q) - return languages.map((lang) => lang.locale) -} - -function convertLanguageCode(language) { - const index = language.indexOf('-') - return index === -1 ? language : language.slice(0, index) -} - -router.post( - '/', - customerServiceOnly, - check('text').isString().trim().isLength({ min: 1 }), - catchError(async (req, res) => { - const { text } = req.body - const languages = getAcceptLanguages(req.header('Accept-Language') || 'zh') - try { - const result = await translate(text, { - to: convertLanguageCode(languages[0]), - }) - res.json({ result }) - } catch (error) { - switch (error.code) { - case '58001': - res.throw(400, 'Target language is not supported') - break - default: - res.throw(500, `${error.code}: ${error.message}`) - break - } - } - }) -) - -module.exports = router diff --git a/api/translate/utils.js b/api/translate/utils.js deleted file mode 100644 index 7352db5c1..000000000 --- a/api/translate/utils.js +++ /dev/null @@ -1,56 +0,0 @@ -const axios = require('axios').default -const FormData = require('form-data') -const crypto = require('crypto') -const AV = require('leancloud-storage') - -const config = { appId: '', secret: '' } - -async function loadConfig() { - const query = new AV.Query('Config').equalTo('key', 'translate.baidu') - const remoteConfig = (await query.first({ useMasterKey: true }))?.get('value') - if (remoteConfig) { - ;['appId', 'secret'].forEach((key) => { - if (!remoteConfig[key]) { - throw new Error(`[Baidu Translate]: ${key} is missing`) - } - }) - Object.assign(config, remoteConfig) - console.log(`[Baidu Translate]: enabled (appId=${config.appId})`) - } -} - -async function translate(text, { from = 'auto', to = 'zh' } = {}) { - if (!config.appId || !config.secret) { - throw new Error('Baidu translate App ID or Secret is not set') - } - - const salt = crypto.randomBytes(16).toString('hex') - const sign = crypto - .createHash('md5') - .update(config.appId + text + salt + config.secret) - .digest('hex') - - const form = new FormData() - form.append('q', text) - form.append('from', from) - form.append('to', to) - form.append('appid', config.appId) - form.append('salt', salt) - form.append('sign', sign) - - const { data } = await axios.post('https://fanyi-api.baidu.com/api/trans/vip/translate', form, { - headers: form.getHeaders(), - }) - - if (data.error_code && data.error_code !== '52000') { - const error = new Error(data.error_msg) - error.code = data.error_code - throw error - } - - return data.trans_result.map((result) => result.dst) -} - -loadConfig().catch(console.warn) - -module.exports = { translate } diff --git a/modules/Ticket/ReplyCard.js b/modules/Ticket/ReplyCard.js index 22eec5293..d5907a851 100644 --- a/modules/Ticket/ReplyCard.js +++ b/modules/Ticket/ReplyCard.js @@ -39,76 +39,11 @@ function getTextNodes(root) { return result } -/** - * @param {string[]} texts - */ -async function translate(texts) { - if (texts.length === 0) { - return [] - } - const idByText = {} - const dstBySrc = {} - const srcs = [] - texts.forEach((text) => { - if (text.trim() === '') { - dstBySrc[text] = text - return - } - if (text in idByText) { - return - } - idByText[text] = srcs.length - srcs.push(text) - }) - const { result: dsts } = await fetch(`/api/1/translate`, { - method: 'POST', - body: { text: srcs.join('\n') }, - }) - dsts.forEach((dst, id) => (dstBySrc[srcs[id]] = dst)) - return texts.map((text) => dstBySrc[text]) -} - export function BaiduTranslate({ enabled, children }) { - const { addNotification } = useContext(AppContext) - const $container = useRef() - const $texts = useRef() - const $task = useRef() - - useEffect(() => { - if (enabled) { - if (!$texts.current) { - $texts.current = getTextNodes($container.current).map((node) => ({ - node, - src: _.trim(node.nodeValue, '\n'), - dst: '', - })) - } - if (!$task.current) { - $task.current = translate($texts.current.map((text) => text.src)).then((results) => { - results.forEach((dst, index) => ($texts.current[index].dst = dst)) - return - }) - } - $task.current - .then(() => { - $texts.current.forEach(({ node, dst }) => { - node.replaceData(0, node.length, dst) - }) - return - }) - .catch((error) => { - $task.current = undefined - addNotification(error) - }) - } else { - if ($texts.current) { - $texts.current.forEach(({ node, src }) => node.replaceData(0, node.length, src)) - } - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [enabled]) - - return