單詞翻譯常見於APP中,那麼在網頁中對於一段中英混雜的內容怎麼準確的做到單詞的翻譯呢?
我要渲染的內容是一段段的html,用react渲染一段html內容是沒有什麼難度,使用dangerouslySetInnerHTML 即可。但是可以做到將內容中的英文單詞翻譯出來,是怎麼實現的呢?一起來看一下吧。
效果
事情是這樣的…
我前端是做內容展示,後臺將Html格式的內容傳遞到前端,前端原生元素的dangerouslySetInnerHTML 屬性去解析html內容,就可以使用react框架渲染html了
仔細看,這個屬性用的是{{ }}2個括號而不是1個括號。原因是:第一{}代表jsx語法開始,第二個是代表dangerouslySetInnerHTML接收的是一個物件鍵值對。它接收的內容是html的,很容易收到XSS攻擊,所以這個屬性有了dangerous這個單詞…
過程
- 重組英文單詞
後臺傳給我的一段一段的內容是這樣的:
既然要提取出來做單詞翻譯,就得有能力去獲取每一個單詞,所以我的打算是,把內容提取出來,是一個英文單詞就放進一個span標籤中。但是這一步在哪裡處理比較好呢,我想了想,在頁面渲染之前處理吧, 這樣頁面渲染的壓力要減小,提高渲染速度。所以我在前端接收到後端傳送的資料之後,在存入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
})
由於傳給我的是一大段內容,裡面的元素的型別不只一種、類名也不止一種,有div .caseTitle .caseContent strong等等,但是我只處理面積最大的一塊英文,也就是隻處理caseContent中的英文單詞這就好辦了。我沒有用string的方法,去查詢這個串在什麼位置,怎麼擷取怎麼拼接。沒有。我利用了DOM的原理,藉助DOM原生的api幫助我得到我要的英文單詞。我建立了一個div(沒有DOM我就自己建立DOM咯),然後DOM查詢.caseContent ,用空格把其中的所有單詞提取出來,再給每一個單詞用span包起來,然後把.caseContent中的內容替換掉,同時div的innerHTML也就變了,最後改變paragraphContent。就這樣把後端傳過來的東西做了修改,再使用。
- 提取單詞
由於我想要翻譯的單詞是用span包裹的,所以我需要檢查使用者點選螢幕所在的節點是不是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,}$/
提取出來英文單詞,但是有些單詞末尾會帶著英文狀態下的逗號,句號.,所以還需要用substring
剪下一下單詞,再呼叫方法。
這裡,提取頁面中點選的內容,需要CSS的配合。
user-select: text;
user-selct: text;可以讓頁面中的內容被選中。而 user-select: none是讓頁面中的內容不被選中。
-ms-user-select: none;
-webkit-user-select: none;
user-select: none;
獲取了單詞s之後,還需要做一點互動,就是被選中的單詞高亮起來,所有,span元素也需要被處理,為了嚴謹,所以需要再判斷是不是單詞,是的話,再對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方法中呼叫了查單詞的api,我用的是有道智雲的api,需要自己註冊一個賬號,然後申請一個應用。獲取appKey和appSecret,設定好from 和 to 的值,也就是你要從什麼語言轉什麼語言,準備一個隨機數sale, 然後各種生成簽名。。
var appSecret = `GOPjZoiSnH592P31Qn6xoallHn3zUnSh`;
var appKey = `06fc15a9c06cb290`;
var salt = `` + (new Date).getTime();
// 多個query可以用
連線 如 query=`apple
orange
banana
pear`
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}`
有了這個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>
後來發現扇貝的單詞api做的也不錯,沒有有道用的這麼麻煩,有道還需要簽名,使用者量大的時候也會有限制,準備以後換扇貝的。
至此,使用react框架完成移動頁面的定向單詞翻譯已完成。其中配合了CSS,DOM,正則,使用別家api等知識,算是一次小小的綜合考核吧,不知道有沒有其他同學也做過這樣的事情,如果有的話,可以交流一下啊。