基於jQuery及騰訊NLP AI平臺的Laravel多語言站點實踐

laravelai發表於2020-12-27

一. 簡介

作為一個兼職碼農,最近嘗試了自己的一個站點的多語言化。發現Laravel本身的locale設定(通過message或者json)適合靜態內容,對於文章等動態內容的多語言化還不能滿足。因此嘗試了通過jQuery結合NLP平臺來實現動態內容翻譯,已經基本實現功能。

二. Laravel部分

1) 在.env中設定預設語言和語言字首

APP_LOCALE=en
APP_LOCAL_PREFIX=

APP_LOCAL_PREFIX用於設定URL的預設語言字首,如果對於預設語言,不希望有語言字首,可以設定為空。

2)新建一個處理locale的middleware,並完成註冊。

public function handle(Request $request, Closure $next)
{

    $locale=in_array($request->segment(1),['en','zh','jp','fr','de','kr'])?  $request->segment(1):env('APP_LOCALE');

    //Set locale
    app()->setLocale($locale);
    //Use session to store the prefix

    if($locale===env('APP_LOCALE') and env('APP_LOCAL_PREFIX')=="")
        session(['locale_prefix' => '']);
    else
        session(['locale_prefix' => $locale]);
    return $next($request);
}

session('locale_prefix')可以用來在連結的URL中動態增加prefix。

3)完成Route更新

  Route::group([ 'middleware' => 'setlocale'], function() {
        Route::get('/', function () {
            return view('welcome');
        });
  });

  Route::group(['prefix' => '{locale}', 'middleware' => 'setlocale'], function() {
        Route::any('/', function(){
            return view('welcome');
        });
        Route::any('/en', 'GMA\WebController@index')->name('home');
  });

對於預設語言,如果不希望有prefix,則Route需要對於有prefix和沒有prefix兩種邏輯分別處理。如果對於預設語言也需要prefix,則僅需保留第二個Route Group。

4)NLP API,這裡採用騰訊AI平臺,程式碼不再贅述,Route設定路徑是/api/nlp

三. jQuery部分

var transObjects = $("[id^='trans']");
var text_array=new Array(transObjects.length);
for(let i=0;i<transObjects.length;i++){
    text_array[i]=new Array();
    //Clean each jQuery childNode's HTML data and collect the text into an array
    getNodeText(transObjects[i],text_array[i]);
    text_array[i]=text_array[i].join("\n");
}

// Join text from different divs into one string
var srcText = text_array.join('\n\n');
var transData={
    'source'     : source_lang,
    'target'     : target_lang,
    'text'       : srcText,
}
//Use Ajax to get translated text via Tencent AI Platform
var getTrans=$.ajax({
    type: 'POST',
    url: '/api/nlp',
    data: transData,
    success: function(data){
        //split into contents for div
        let responseText=data.data.target_text
        text_array=responseText.split('\n\n')
        //Processing each div
        for(let i=0;i<transObjects.length;i++){
            //split translated text into different jQuery childNodes
            text_array[i]=text_array[i].split('\n');
            updateNodeText(transObjects[i],text_array[i]);
        }
     },
    dataType: 'json',
});

//Covert all jQuery childNodes' text to array
function getNodeText(TransObj,text_array){
    for(let i=0;i<TransObj.childNodes.length;i++){
        if(TransObj.childNodes[i].childNodes.length>0){
            getNodeText(TransObj.childNodes[i],text_array);
        }
        else{
            if(TransObj.childNodes[i].nodeName==="#text"){
                let parsedText=delExtraSpaces(delNewlines(TransObj.childNodes[i].data))if(parsedText!==""  && parsedText!==" ") text_array.push(parsedText);
            } 
        }
    }
}

//Update jQuery childNodes with translated text
function updateNodeText(TransObj,text_array){
    for(let i=0;i<TransObj.childNodes.length;i++)
{
        if(TransObj.childNodes[i].childNodes.length>0){
            updateNodeText(TransObj.childNodes[i],text_array);
        }
       else{
           if(TransObj.childNodes[i].nodeName==="#text"){
                let parsedText=delExtraSpaces(delNewlines(TransObj.childNodes[i].data)).trim();
                let sentenseEnd=['.','。','?','!'];
                if(parsedText!=="" && parsedText!==" "){
                if(text_array[0]!==undefined){
                    //check the last character of string, remove the . introduced by new line
                    if(!sentenseEnd.includes(parsedText.charAt(parsedText.length-1)) && 
                        sentenseEnd.includes(text_array[0].charAt(text_array[0].length-1)) ){
                        text_array[0]=" "+text_array[0].substring(0,text_array[0].length-2)+parsedText.charAt(parsedText.length-1)+" ";
                    }
                    TransObj.childNodes[i].data=" "+text_array[0];
                    } 
                    text_array.shift();
                } 
            } 
        }
    }
}

//Remove all new line breaks
function delNewlines(nodeText){
    if(nodeText.includes('\n')){
        return delNewlines(nodeText.replace('\n',''));
    }
    return nodeText;
}
//Remove extra white spaces
function delExtraSpaces(nodeText){
    if(nodeText.includes('  ')){
        return delExtraSpaces(nodeText.replace('  ',' '));
    }
    return nodeText;
}

基本原理是通過jQuery獲取所有childNodes的內容,對於其中的文字內容進行預處理(刪除多餘的空格和所有換行符),然後用換行符作為拼接各個childNode內容的分隔符。拼接成一段文字後,通過ajax提交給NLP API翻譯,獲取翻譯內容後再用換行符分隔各個childNodes,然後更新內容。

四. 完整程式碼

完成程式碼見Github

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章