單詞翻譯常見于APP中秆剪,那么在網(wǎng)頁中對于一段中英混雜的內(nèi)容怎么準(zhǔn)確的做到單詞的翻譯呢赊淑?
我要渲染的內(nèi)容是一段段的html,用react渲染一段html內(nèi)容是沒有什么難度仅讽,使用dangerouslySetInnerHTML 即可陶缺。但是可以做到將內(nèi)容中的英文單詞翻譯出來,是怎么實(shí)現(xiàn)的呢何什?一起來看一下吧组哩。
效果
事情是這樣的...
我前端是做內(nèi)容展示,后臺將Html格式的內(nèi)容傳遞到前端处渣,前端原生元素的dangerouslySetInnerHTML 屬性去解析html內(nèi)容,就可以使用react框架渲染html了
仔細(xì)看蛛砰,這個(gè)屬性用的是{{ }}2個(gè)括號而不是1個(gè)括號罐栈。原因是:第一{}代表jsx語法開始,第二個(gè)是代表dangerouslySetInnerHTML接收的是一個(gè)對象鍵值對泥畅。它接收的內(nèi)容是html的荠诬,很容易收到XSS攻擊,所以這個(gè)屬性有了dangerous這個(gè)單詞...
過程
- 重組英文單詞
后臺傳給我的一段一段的內(nèi)容是這樣的:
既然要提取出來做單詞翻譯位仁,就得有能力去獲取每一個(gè)單詞柑贞,所以我的打算是,把內(nèi)容提取出來聂抢,是一個(gè)英文單詞就放進(jìn)一個(gè)span標(biāo)簽中钧嘶。但是這一步在哪里處理比較好呢,我想了想琳疏,在頁面渲染之前處理吧有决, 這樣頁面渲染的壓力要減小闸拿,提高渲染速度。所以我在前端接收到后端發(fā)送的數(shù)據(jù)之后书幕,在存入store之前就先處理好新荤。
case "OBT_BOOK_CONTENT_SUCCESS":
var newContents = action.meta.bookcontent.map((item, index) => {
item.paragraphContent = item.paragraphContent.replace(/src="/g, `src="${url}`);
//處理caseContent中的單詞
var div = document.createElement('div');
div.innerHTML = item.paragraphContent;
var caseContent = div.querySelector('.caseContent');
if (caseContent) {
var arr = caseContent.innerText.split(" ");
for (var i = 0; i < arr.length; i++) {
arr[i] = "<span>" + arr[i] + "</span>"
}
caseContent.innerHTML = arr.join("");
item.paragraphContent = div.innerHTML;
}
return item;
})
return Object.assign({}, state, {
bookcontent: newContents
})
由于傳給我的是一大段內(nèi)容,里面的元素的類型不只一種台汇、類名也不止一種苛骨,有div .caseTitle .caseContent strong等等,但是我只處理面積最大的一塊英文苟呐,也就是只處理caseContent中的英文單詞這就好辦了痒芝。我沒有用string的方法,去查找這個(gè)串在什么位置掠抬,怎么截取怎么拼接吼野。沒有。我利用了DOM的原理两波,借助DOM原生的api幫助我得到我要的英文單詞瞳步。我創(chuàng)建了一個(gè)div(沒有DOM我就自己創(chuàng)建DOM咯),然后DOM查找.caseContent腰奋,用空格把其中的所有單詞提取出來单起,再給每一個(gè)單詞用span包起來,然后把.caseContent中的內(nèi)容替換掉,同時(shí)div的innerHTML也就變了劣坊,最后改變paragraphContent嘀倒。就這樣把后端傳過來的東西做了修改,再使用局冰。
- 提取單詞
由于我想要翻譯的單詞是用span包裹的测蘑,所以我需要檢查用戶點(diǎn)擊屏幕所在的節(jié)點(diǎn)是不是span
if(e.target.nodeName === 'SPAN'){
var s = "";
if(e.target.innerText) {
var len = e.target.innerText.length;
if( !/^[\u4e00-\u9fa5]{0,}$/.test(e.target.innerText) ){
if(e.target.innerText[len -1 ] === ',' || e.target.innerText[len -1 ] === '.' ){
s = e.target.innerText.substring(0, len -1 )
}else {
s = e.target.innerText;
}
var chooseSpan = e.target;
this.props.checkWords(s, chooseSpan);
}
}
console.log( s);
}
利用正則!/^[\u4e00-\u9fa5]{0,}$/
提取出來英文單詞,但是有些單詞末尾會帶著英文狀態(tài)下的逗號,句號.康二,所以還需要用substring
剪切一下單詞碳胳,再調(diào)用方法。
這里沫勿,提取頁面中點(diǎn)擊的內(nèi)容挨约,需要CSS的配合。
user-select: text;
user-selct: text;可以讓頁面中的內(nèi)容被選中产雹。而 user-select: none是讓頁面中的內(nèi)容不被選中诫惭。
-ms-user-select: none;
-webkit-user-select: none;
user-select: none;
獲取了單詞s之后,還需要做一點(diǎn)交互蔓挖,就是被選中的單詞高亮起來夕土,所有,span元素也需要被處理时甚,為了嚴(yán)謹(jǐn)隘弊,所以需要再判斷是不是單詞哈踱,是的話,再對span 做處理梨熙。
checkWords(txt, selectedSpan) {
// 查單詞
this.selectedSpan = selectedSpan;
if (txt.toString().length > 1) {
if (/^\w+$/.test(txt)) {
this.props.getWord(txt);
this.setState({
showWord: true
})
selectedSpan.style.color="#fff";
selectedSpan.style.backgroundColor="rgb(0,153,223)";
}
}
}
- 請求翻譯接口
getWord方法中調(diào)用了查單詞的api,我用的是有道智云的api开镣,需要自己注冊一個(gè)賬號,然后申請一個(gè)應(yīng)用咽扇。獲取appKey和appSecret,設(shè)置好from 和 to 的值邪财,也就是你要從什么語言轉(zhuǎn)什么語言,準(zhǔn)備一個(gè)隨機(jī)數(shù)salt质欲, 然后各種生成簽名一波操作树埠。
var appSecret = 'GOPjZoiSnH592P31Qn6xoallHn3zUnSh';
var appKey = '06fc15a9c06cb290';
var salt = '' + (new Date).getTime();
// 多個(gè)query可以用\n連接 如 query='apple\norange\nbanana\npear'
var from = 'en';
var to = 'zh-CHS';
var str1 = appKey + q + salt + appSecret;
var data = null;
var sign = md5(str1);
var sendRes = res;
sign = sign.toUpperCase();
q = encodeURI(q);
var url = `http://openapi.youdao.com/api?q=${q}&from=${from}&to=${to}&appKey=${appKey}&salt=${salt}&sign=${sign}`
有了這個(gè)url之后,就可以請求了嘶伟。返回的東西是:
播放單詞的地方就是用 H5的audio 元素,src是
http://dict.youdao.com/dictvoice?audio=${this.props.word.query}
<audio id="audio" >
<source src={voiceUrl} type="audio/mp3"></source>
</audio>
后來發(fā)現(xiàn)扇貝的單詞api做的也不錯(cuò)怎憋,沒有有道用的這么麻煩,有道還需要簽名九昧,用戶量大的時(shí)候也會有限制绊袋,準(zhǔn)備以后換扇貝的。
完畢
至此铸鹰,使用react框架完成移動頁面的定向單詞翻譯已完成癌别。其中配合了CSS,DOM,正則蹋笼,使用別家api等知識展姐,算是一次小小的綜合考核吧,不知道有沒有其他同學(xué)也做過這樣的事情剖毯,如果有的話圾笨,可以交流一下啊。