NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name LNMTL - Improved translation using Sogou
// @namespace sglnmt
// @match https://lnmtl.com/chapter/*
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// @require https://userscripts-mirror.org/scripts/source/107941.user.js#sha384=Q8t880BurrlGKTdpvYv2+da12PYnvljdiU8aJvakk1uE3QMbzb190ueXNpAUY98p
// @version 1.0.7
// @author Spawner + mmtf
// @description 3/16/2020, 6:10:03 PM
// @license MIT
// ==/UserScript==
/*
CHANGELOG:
1.0 - Initial Release
1.0.1 - Small Optimizations
1.0.2 - Added word color highlighter and changed dialog style
1.0.3 - Plugin Settings (Credits: mmtf)
1.0.4 - Code fix + Added Button for Translation
1.0.5 - Switch to GM_xmlhttpRequest to fix CORS error + Show/Hide translation
1.0.6 - Bug fixes
1.0.7 - Removed the private API since it's no longer working + using a different approach.
NOTE:
- Don't worry about the API (key & id), Sogou just needs a valid/existing hash.
- I also want to thank mmtf because I have no experience in JS+CSS or web dev in general, so I shamelessly copied parts of his script.
*/
/* CSS STYLES */
GM_addStyle("hgltr { text-shadow: 0 0 10px #e74c3c, 0 0 10px #e74c3c }");
GM_addStyle(".sgt { color:white; font-size: 2.2rem; margin-bottom:42px; font-family: Roboto }");
/* VARIABLES */
var uniqueWords = new Map();
var autoSogouOn = GM_SuperValue.get('autoSogouOn' , false);
var autoShowOn = GM_SuperValue.get('autoShowOn' , false);
var autoGlowOn = GM_SuperValue.get('autoGlowOn' , true);
var chunk = [];
var translatedChunks = [];
var translatePromises = [];
var isTranslated = false;
window.onload = function ()
{
replaceRaws();
createBtnUI();
createSettings();
$( '.original' ).css( 'fontSize', '1.9rem' );
$( '.original' ).css( 'color', 'rgb(128, 142, 155)' );
$( '.translated t' ).each( function ( index )
{
var value = $( this )
.attr( 'data-title', $( this ).text() )
.text()
.trimLeft();
uniqueWords.set( value, index );
} );
chunks = seperateIntoChunks( getRawParagraphs() );
chunks[ 0 ] = chunks[ 0 ].trimLeft();
console.log(chunks.length);
if ( autoSogouOn )
doWork();
if( autoShowOn )
$( '.btn.btn-primary.js-toggle-translated' )[ 0 ].click();
}
async function doWork()
{
for ( var id = 0; id < chunks.length; id++ )
{
// We must set the cookies on each send
// if not, it will trigger their bot system guard.
await sogouSetCookies();
translatedChunks[ id ] = await sogouTranslator( chunks[ id ] );
}
isTranslated = true;
var finalResult = seperateChunksIntoPars( translatedChunks );
createSentence( finalResult );
}
function sogouTranslator( text )
{
const userId = () => {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace( /[xy]/g, function ( name ) {
var M = 0 | 16 * Math.random();
var pid = "x" == name ? M : 8 | 3 & M;
return pid.toString( 16 );
} );
};
return new Promise( ( resolve ) =>
{
var formData =
{
'from' : 'zh-CHS',
'to' : 'en',
'text' : text,
'client' : 'pc',
'fr' : 'browser_pc',
'pid' : 'sogou-dict-vr',
'dict' : 'true',
'word_group' : 'true',
'second_query': 'true',
'uuid' : userId,
'needQc' : '1',
's' : md5( 'zh-CHS' + 'en' + text + '8511813095152' )
};
GM_xmlhttpRequest(
{
method : "POST",
url : "https://fanyi.sogou.com/reventondc/translateV2",
data : $.param( formData ),
headers:
{
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Accept" : "application/json",
"Referer" : "https://fanyi.sogou.com/"
}
,
onload: function ( result )
{
var jsonObj = JSON.parse( result.response );
resolve( jsonObj.data.translate.dit );
}
} );
});
}
function sogouSetCookies()
{
var dateExpires = new Date;
var match = "sogou.com";
var id = "1"; // Must be randomized in the next version
return new Promise( ( resolve ) =>
{
GM_xmlhttpRequest(
{
method : "GET",
url : "https://fanyi.sogou.com/",
headers:
{
"Cookie" : setCookie("SNUID", id, dateExpires.toGMTString(), match, "/")
}
,
onload: function ( result )
{
resolve( result )
}
});
});
}
function setCookie(a, val, url, c, name)
{
return a = [a, "=", val], url && a.push(";expires=", url), c && a.push(";domain=", c), name && a.push(";path=", name), document.cookie = a.join(""), a.join("");
}
function createSentence( gtpars )
{
gtpars.forEach( ( sentence, index ) =>
{
if ( autoGlowOn )
{
for ( const [ key, value ] of uniqueWords.entries() )
sentence = sentence.replace( new RegExp( '(\\b)(' + key + ')(\\b)', 'gi' ), '<hgltr style="color:orange">' + key + '</hgltr>' );
}
var quotesStyle =
( sentence.startsWith( '"' ) && sentence.endsWith( '"' ) ) ? 'style="color:rgb(22, 160, 133);font-style:italic;font-weight:bold"': "";
$( '.translated' ).eq( index ).after( "<div class='sgt' " + quotesStyle + " tab-index=0><sentence data-index=" + index + ">" + sentence + "</sentence></div>" );
});
}
function createBtnUI ()
{
$( '.js-toggle-original' ).after( '<button class="btn btn-enabled js-toggle-sgt">SGT</button>' );
$( '.btn.btn-enabled.js-toggle-sgt' )[0].style.borderColor = "white";
$( '.btn.btn-enabled.js-toggle-sgt' )[0].style.boxShadow = "0px 0px 20px #ecf0f1";
$( '.btn.btn-enabled.js-toggle-sgt' )[0].style.color = "orange";
$( '.js-toggle-sgt' ).click( function ()
{
if ( isTranslated )
$( ".sgt" ).fadeToggle( 'fast', 'linear' );
if ( !autoSogouOn && !isTranslated )
doWork();
} )
.text( 'SGT' ).addClass( 'btn-default' );
}
function createSettings()
{
let title = $( '<h3> Improved Translation Settings </h3>' );
let checked = autoSogouOn ? 'checked' : '';
let optionAutoswitch = $( '<sub><input id="autoSogouOn" type="checkbox" ' + checked + '></sub> <label for="autoSogouOn">Automatically display Sogou translation after loading the page.</label>' )
.on( 'change', function ()
{
autoSogouOn = $( '#autoSogouOn' )[ 0 ].checked;
GM_SuperValue.set( 'autoSogouOn', autoSogouOn );
});
let checked2 = autoGlowOn ? 'checked' : '';
let optionGlow = $( '<sub><input id="autoGlowOn" type="checkbox" ' + checked2 + '></sub> <label for="autoGlowOn">Apply the Glow word highlighter.</label>' )
.on( 'change', function ()
{
autoGlowOn = $( '#autoGlowOn' )[ 0 ].checked;
GM_SuperValue.set( 'autoGlowOn', autoGlowOn );
});
let checked3 = autoShowOn ? 'checked' : '';
let optionShow = $( '<sub><input id="autoShowOn" type="checkbox" ' + checked3 + '></sub> <label for="autoShowOn"><span style="color:rgb(26, 188, 156)">Show only Translation.</span></label>' )
.on( 'change', function ()
{
autoShowOn = $( '#autoShowOn' )[ 0 ].checked;
GM_SuperValue.set( 'autoShowOn', autoShowOn );
});
let row = $( '<div class="row"/>' );
$( "#chapter-display-options-modal .modal-body" )
.append( title )
.append( optionAutoswitch ).append( '<br>' )
.append( optionGlow ).append( '<br>' )
.append( optionShow ).append( '<br>' )
.append( '<span style="color:yellow">Changes apply after the page is reloaded.</span>' )
.append( row );
}
function replaceRaws()
{
$( '.original t' ).each( function ()
{
var mytext = $( this ).text();
$( this ).text( $( this ).attr( 'data-title' ).replace( /\(\w+\)/g, '' ) );
$( this ).attr( 'data-title', mytext );
});
// add whitespace between two english words
$( '.original' ).find( 't' ).filter( function ( index )
{
var prev = $( this ).get( 0 ).previousSibling;
return prev ? $( this ).get( 0 ).previousSibling.nodeName == 'T': false;
}).each( function ()
{
$( this ).text( ' ' + $( this ).text() );
});
}
function seperateIntoChunks( paragraphs )
{
let chunks = [];
let currentchunk = "";
for ( let i = 0; i < paragraphs.length; i++ )
{
if ( ( currentchunk + paragraphs[ i ] ).length >= 4000 )
{
chunks.push( currentchunk );
currentchunk = paragraphs[ i ];
}
else
currentchunk = currentchunk + "\n\n" + paragraphs[ i ];
}
if ( paragraphs.length != 0 )
chunks.push( currentchunk );
return chunks;
}
function seperateChunksIntoPars( chunks )
{
let pars = [];
chunks.forEach( ( chunk ) => chunk.split( '\n\n' ).forEach( ( par ) => pars.push( par ) ) );
return pars;
}
function getRawParagraphs()
{
return $( '.original' ).text().trim().split( '\n' )
}
function md5( str )
{
var k = [],
i = 0;
for ( i = 0; i < 64; )
k[ i ] = 0 | ( Math.abs( Math.sin( ++i ) ) * 4294967296 );
var b, c, d, j,
x = [],
str2 = unescape( encodeURI( str ) ),
a = str2.length,
h = [ ( b = 1732584193 ), ( c = -271733879 ), ~b, ~c ];
for ( i = 0; i <= a; )
x[ i >> 2 ] |= ( str2.charCodeAt( i ) || 128 ) << ( 8 * ( i++ % 4 ) );
x[ ( str = ( ( a + 8 ) >> 6 ) * 16 + 14 ) ] = a * 8;
i = 0;
for ( ; i < str; i += 16 )
{
a = h;
j = 0;
for ( ; j < 64; )
{
a = [
( d = a[ 3 ] ),
( b = a[ 1 ] | 0 ) +
( ( ( d =
a[ 0 ] + [
( b & ( c = a[ 2 ] ) ) | ( ~b & d ),
( d & b ) | ( ~d & c ),
b ^ c ^ d,
c ^ ( b | ~d )
][ ( a = j >> 4 ) ] +
( k[ j ] + ( x[ ( [ j, 5 * j + 1, 3 * j + 5, 7 * j ][ a ] % 16 ) + i ] | 0 ) ) ) <<
( a = [ 7, 12, 17, 22, 5, 9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21 ][
4 * a + ( j++ % 4 )
] ) ) |
( d >>> ( 32 - a ) ) ),
b,
c
];
}
for ( j = 4; j; )
{
h[ --j ] = h[ j ] + a[ j ];
}
}
str = "";
for ( ; j < 32; )
{
str += ( ( h[ j >> 3 ] >> ( ( 1 ^ ( j++ & 7 ) ) * 4 ) ) & 15 ).toString( 16 );
}
return str;
}