diff --git a/src/apis/index.js b/src/apis/index.js index 57a3c3f..31d3af5 100644 --- a/src/apis/index.js +++ b/src/apis/index.js @@ -3,6 +3,7 @@ import { fetchData } from "../libs/fetch"; import { URL_CACHE_TRAN, URL_CACHE_DELANG, + URL_CACHE_BINGDICT, KV_SALT_SYNC, OPT_LANGS_TO_SPEC, OPT_LANGS_SPEC_DEFAULT, @@ -107,6 +108,60 @@ export const apiMicrosoftLangdetect = async (text) => { return ""; }; +/** + * Microsoft词典 + * @param {*} text + * @returns + */ +export const apiMicrosoftDict = async (text) => { + const cacheOpts = { text }; + const cacheInput = `${URL_CACHE_BINGDICT}?${queryString.stringify(cacheOpts)}`; + const cache = await getHttpCachePolyfill(cacheInput); + if (cache) { + return cache; + } + + const host = "https://cn.bing.com"; + const url = `${host}/dict/search?q=${text}`; + const str = await fetchData(url, {}, { useCache: false }); + + const parser = new DOMParser(); + const doc = parser.parseFromString(str, "text/html"); + + const word = doc.querySelector("#headword > h1").textContent.trim(); + if (!word) { + return null; + } + + const trs = []; + doc.querySelectorAll("div.qdef > ul > li").forEach(($li) => { + const pos = $li.querySelector(".pos")?.textContent?.trim(); + const def = $li.querySelector(".def")?.textContent?.trim(); + trs.push({ pos, def }); + }); + + const aus = []; + const $audioUS = doc.querySelector("#bigaud_us"); + const $audioUK = doc.querySelector("#bigaud_uk"); + if ($audioUS) { + const audioUS = host + $audioUS?.dataset?.mp3link; + const $phoneticUS = $audioUS.parentElement?.previousElementSibling; + const phoneticUS = $phoneticUS?.textContent?.trim(); + aus.push({ key: "US", audio: audioUS, phonetic: phoneticUS }); + } + if ($audioUK) { + const audioUK = host + $audioUK?.dataset?.mp3link; + const $phoneticUK = $audioUK.parentElement?.previousElementSibling; + const phoneticUK = $phoneticUK?.textContent?.trim(); + aus.push({ key: "UK", audio: audioUK, phonetic: phoneticUK }); + } + + const res = { word, trs, aus }; + putHttpCachePolyfill(cacheInput, null, res); + + return res; +}; + /** * 百度语言识别 * @param {*} text diff --git a/src/config/api.js b/src/config/api.js index 6fa0e96..5c7c512 100644 --- a/src/config/api.js +++ b/src/config/api.js @@ -14,8 +14,9 @@ export const INPUT_PLACE_KEY = "{{key}}"; // 占位符 export const INPUT_PLACE_MODEL = "{{model}}"; // 占位符 export const OPT_DICT_BAIDU = "Baidu"; +export const OPT_DICT_BING = "Bing"; export const OPT_DICT_YOUDAO = "Youdao"; -export const OPT_DICT_ALL = [OPT_DICT_BAIDU, OPT_DICT_YOUDAO]; +export const OPT_DICT_ALL = [OPT_DICT_BING, OPT_DICT_YOUDAO]; export const OPT_DICT_MAP = new Set(OPT_DICT_ALL); export const OPT_SUG_BAIDU = "Baidu"; diff --git a/src/config/url.js b/src/config/url.js index 9b03e60..83b8bac 100644 --- a/src/config/url.js +++ b/src/config/url.js @@ -2,6 +2,7 @@ import { APP_LCNAME } from "./app"; export const URL_CACHE_TRAN = `https://${APP_LCNAME}/translate`; export const URL_CACHE_DELANG = `https://${APP_LCNAME}/detectlang`; +export const URL_CACHE_BINGDICT = `https://${APP_LCNAME}/bingdict`; export const URL_KISS_WORKER = "https://github.com/fishjar/kiss-worker"; export const URL_KISS_PROXY = "https://github.com/fishjar/kiss-proxy"; diff --git a/src/views/Options/FavWords.js b/src/views/Options/FavWords.js index 5200371..9d70bc0 100644 --- a/src/views/Options/FavWords.js +++ b/src/views/Options/FavWords.js @@ -22,6 +22,7 @@ import { apiTranslate } from "../../apis"; import { OPT_TRANS_BAIDU, PHONIC_MAP } from "../../config"; import { useConfirm } from "../../hooks/Confirm"; import { useSetting } from "../../hooks/Setting"; +import { DICT_MAP } from "../Selection/DictMap"; function FavAccordion({ word, index }) { const [expanded, setExpanded] = useState(false); @@ -55,6 +56,7 @@ function FavAccordion({ word, index }) { export default function FavWords() { const i18n = useI18n(); const { favList, wordList, mergeWords, clearWords } = useFavWords(); + const { setting } = useSetting(); const confirm = useConfirm(); const handleImport = (data) => { @@ -80,41 +82,22 @@ export default function FavWords() { }; const handleTranslation = async () => { + const { enDict } = setting?.tranboxSetting; + const dict = DICT_MAP[enDict]; + if (!dict) return ""; + const tranList = []; - for (const text of wordList) { + for (const word of wordList) { try { - // todo: 修复 - const dictRes = await apiTranslate({ - text, - translator: OPT_TRANS_BAIDU, - fromLang: "en", - toLang: "zh-CN", - }); - if (dictRes[2]?.type === 1) { - tranList.push(JSON.parse(dictRes[2].result)); - } + const data = await dict.apiFn(word); + const tran = dict.toText(data); + tranList.push([word, tran].join("\n")); } catch (err) { // skip } } - return tranList - .map((dictResult) => - [ - `## ${dictResult.src}`, - dictResult.voice - ?.map(Object.entries) - .map((item) => item[0]) - .map(([key, val]) => `${PHONIC_MAP[key]?.[0] || key} ${val}`) - .join(" "), - dictResult.content[0].mean - .map(({ pre, cont }) => { - return ` - ${pre ? `[${pre}] ` : ""}${Object.keys(cont).join("; ")}`; - }) - .join("\n"), - ].join("\n\n") - ) - .join("\n\n"); + return tranList.join("\n\n"); }; return ( diff --git a/src/views/Selection/AudioBtn.js b/src/views/Selection/AudioBtn.js index 3d5d052..17810bb 100644 --- a/src/views/Selection/AudioBtn.js +++ b/src/views/Selection/AudioBtn.js @@ -1,9 +1,9 @@ import IconButton from "@mui/material/IconButton"; import VolumeUpIcon from "@mui/icons-material/VolumeUp"; -import { useTextAudio } from "../../hooks/Audio"; +import { useAudio } from "../../hooks/Audio"; -export default function AudioBtn({ text, lan = "uk" }) { - const { error, ready, playing, onPlay } = useTextAudio(text, lan); +export default function AudioBtn({ src }) { + const { error, ready, playing, onPlay } = useAudio(src); if (error || !ready) { return ( diff --git a/src/views/Selection/DictCont.js b/src/views/Selection/DictCont.js index e3b893b..06e714e 100644 --- a/src/views/Selection/DictCont.js +++ b/src/views/Selection/DictCont.js @@ -1,90 +1,28 @@ -import { useState, useEffect } from "react"; +import { useState, useEffect, useMemo } from "react"; import Stack from "@mui/material/Stack"; import FavBtn from "./FavBtn"; import Typography from "@mui/material/Typography"; -import AudioBtn from "./AudioBtn"; import CircularProgress from "@mui/material/CircularProgress"; import Divider from "@mui/material/Divider"; import Alert from "@mui/material/Alert"; -import { OPT_DICT_BAIDU, OPT_DICT_YOUDAO, PHONIC_MAP } from "../../config"; import CopyBtn from "./CopyBtn"; import { useAsyncNow } from "../../hooks/Fetch"; -import { apiYoudaoDict } from "../../apis"; +import { DICT_MAP } from "./DictMap"; -function DictBaidu({ text, setCopyText }) { - // useEffect(() => { - // if (!data) { - // return; - // } - - // const copyText = [ - // data.src, - // data.voice - // ?.map(Object.entries) - // .map((item) => item[0]) - // .map(([key, val]) => `${PHONIC_MAP[key]?.[0] || key} ${val}`) - // .join(" "), - // data.content[0].mean - // .map(({ pre, cont }) => { - // return `${pre ? `[${pre}] ` : ""}${Object.keys(cont).join("; ")}`; - // }) - // .join("\n"), - // ].join("\n"); - - // setCopyText(copyText); - // }, [data, setCopyText]); - - return baidu dict not supported yet; - - { - /* {dictResult && ( - - - {dictResult.voice - ?.map(Object.entries) - .map((item) => item[0]) - .map(([key, val]) => ( - - {`${PHONIC_MAP[key]?.[0] || key} ${val}`} - - - ))} - - - - {dictResult.content[0].mean.map(({ pre, cont }, idx) => ( - - {pre && `[${pre}] `} - {Object.keys(cont).join("; ")} - - ))} - - - )} */ - } -} - -function DictYoudao({ text, setCopyText }) { - const { loading, error, data } = useAsyncNow(apiYoudaoDict, text); +function DictBody({ text, setCopyText, dict }) { + const { loading, error, data } = useAsyncNow(dict.apiFn, text); useEffect(() => { if (!data) { return; } - const copyText = [ - text, - data?.ec?.word?.trs - ?.map(({ pos, tran }) => `${pos ? `[${pos}] ` : ""}${tran}`) - .join("\n"), - ].join("\n"); - + const copyText = [text, dict.toText(data)].join("\n"); setCopyText(copyText); - }, [data, setCopyText]); + }, [data, text, dict, setCopyText]); + + const uiAudio = useMemo(() => dict.uiAudio(data), [data, dict]); + const uiTrans = useMemo(() => dict.uiTrans(data), [data, dict]); if (loading) { return ; @@ -95,30 +33,20 @@ function DictYoudao({ text, setCopyText }) { } if (!data) { - return; + return Empty result; } return ( - - {data?.ec?.word?.trs?.map(({ pos, tran }, idx) => ( - - {pos && `[${pos}] `} - {tran} - - ))} - + {uiAudio} + {uiTrans} ); } export default function DictCont({ text, enDict }) { const [copyText, setCopyText] = useState(text); - - const dictMap = { - [OPT_DICT_BAIDU]: , - [OPT_DICT_YOUDAO]: , - }; + const dict = DICT_MAP[enDict]; return ( @@ -136,7 +64,7 @@ export default function DictCont({ text, enDict }) { - {dictMap[enDict] || Dict not support} + {dict && } ); } diff --git a/src/views/Selection/DictMap.js b/src/views/Selection/DictMap.js new file mode 100644 index 0000000..2d29eaa --- /dev/null +++ b/src/views/Selection/DictMap.js @@ -0,0 +1,56 @@ +import Typography from "@mui/material/Typography"; +import AudioBtn from "./AudioBtn"; +import { OPT_DICT_BING, OPT_DICT_YOUDAO } from "../../config"; +import { apiMicrosoftDict, apiYoudaoDict } from "../../apis"; + +export const DICT_MAP = { + [OPT_DICT_BING]: { + apiFn: apiMicrosoftDict, + toText: (data) => + data.trs + ?.map(({ pos, def }) => `${pos ? `[${pos}] ` : ""}${def}`) + .join("\n"), + uiAudio: (data) => ( + + {data?.aus.map(({ key, audio, phonetic }) => ( + + {phonetic} + + + ))} + + ), + uiTrans: (data) => ( + + {data?.trs?.map(({ pos, def }, idx) => ( + + {pos && `[${pos}] `} + {def} + + ))} + + ), + }, + [OPT_DICT_YOUDAO]: { + apiFn: apiYoudaoDict, + toText: (data) => + data?.ec?.word?.trs + ?.map(({ pos, tran }) => `${pos ? `[${pos}] ` : ""}${tran}`) + .join("\n"), + uiAudio: () => null, + uiTrans: (data) => ( + + {data?.ec?.word?.trs?.map(({ pos, tran }, idx) => ( + + {pos && `[${pos}] `} + {tran} + + ))} + + ), + }, +};