live-translator-nodeapp/index.js

904 lines
31 KiB
JavaScript
Raw Normal View History

2020-06-18 08:12:56 +00:00
const translation_token_generator = require('google-translate-token');
2020-06-24 02:21:08 +00:00
const replaceAll = require('string.prototype.replaceall');
2020-06-18 08:12:56 +00:00
const bodyParser = require('body-parser');
const express = require('express');
const request = require('request');
const cors = require('cors');
const fs = require('fs');
// if (process.platform == 'win32') {
// const ioHook = require('iohook');
// ioHook.registerShortcut([96], (keys) => {
// silence_timeout_exceeded_action();
// });
// ioHook.registerShortcut([110], (keys) => {
// pause_toggle();
// });
// }
2020-06-18 08:12:56 +00:00
var server = express();
server.use(cors());
server.use(express.urlencoded({ extended: true }));
server.use(bodyParser.json({ extended: true }));
2020-06-18 08:12:56 +00:00
const index_page = `
<!DOCUMENT html>
<html lang="br">
<head>
<meta charset="utf-8">
<title>Tradutor simultâneo</title>
<link rel="stylesheet" type="text/css" href="/main.css" />
<script src="/main.js" defer></script>
</head>
<body>
<div>
<div id="cfgformp">
<h3 class="nospacing">Configurações</h3>
<form id="cfgform" onsubmit="return false;">
<div class="formgroup">
<h5 class="nospacing">Idioma da fala</h5>
2020-06-20 19:39:22 +00:00
<input type="radio" id="ilpt" name="il" value="1" checked>
2020-06-18 08:12:56 +00:00
<label for="ilpt">PT</label><br>
2020-06-20 19:39:22 +00:00
<input type="radio" id="ilen" name="il" value="2">
2020-06-18 08:12:56 +00:00
<label for="ilen">EN</label><br>
2020-06-20 19:39:22 +00:00
<input type="radio" id="iles" name="il" value="3">
2020-06-18 08:12:56 +00:00
<label for="iles">ES</label><br>
</div>
<div class="formgroup">
<h5 class="nospacing">Idioma da 1ª tradução</h5>
2020-06-20 19:39:22 +00:00
<input type="radio" id="1lpt" name="1l" value="1">
2020-06-18 08:12:56 +00:00
<label for="1lpt">PT</label><br>
2020-06-20 19:39:22 +00:00
<input type="radio" id="1len" name="1l" value="2">
2020-06-18 08:12:56 +00:00
<label for="1len">EN</label><br>
2020-06-20 19:39:22 +00:00
<input type="radio" id="1les" name="1l" value="3">
2020-06-18 08:12:56 +00:00
<label for="1les">ES</label><br>
2020-06-20 19:39:22 +00:00
<input type="radio" id="1l_" name="1l" value="0" checked>
2020-06-18 08:12:56 +00:00
<label for="1l_">Ignorar</label><br>
</div>
<div class="formgroup">
<h5 class="nospacing">Idioma da 2ª tradução</h5>
2020-06-20 19:39:22 +00:00
<input type="radio" id="2lpt" name="2l" value="1">
2020-06-18 08:12:56 +00:00
<label for="2lpt">PT</label><br>
2020-06-20 19:39:22 +00:00
<input type="radio" id="2len" name="2l" value="2">
2020-06-18 08:12:56 +00:00
<label for="2len">EN</label><br>
2020-06-20 19:39:22 +00:00
<input type="radio" id="2les" name="2l" value="3">
2020-06-18 08:12:56 +00:00
<label for="2les">ES</label><br>
2020-06-20 19:39:22 +00:00
<input type="radio" id="2l_" name="2l" value="0" checked>
2020-06-18 08:12:56 +00:00
<label for="2l_">Ignorar</label><br>
</div>
<div class="formgroup">
2020-06-21 00:17:05 +00:00
<h5 class="nospacing">Visual</h5>
<label for="ff">Família de fontes:</label>
2020-06-20 20:42:50 +00:00
<input type="text" id="ff" name="fontfamily" value="sans-serif">
<br>
2020-06-21 00:17:05 +00:00
<label for="fs">Tamanho:</label>
<input type="number" step="0.1" id="fs" name="fontsize" value="3"> em
<br>
<label for="bh">Altura:</label>
<input type="number" step="0.1" id="bh" name="boxheight" value="6.6"> ex
<br>
<label for="clr">Cor:</label>
<input type="text" step="0.1" id="clr" name="color" value="#FFFFFF">
<br>
</div>
<div class="formgroup">
<h5 class="nospacing">Controle</h5>
2020-06-20 20:42:50 +00:00
<input type="button" id="apl" value="Aplicar" onclick="return aplicar();">
<br>
<input type="button" id="pause" value="(Des)pausar" onclick="return pausar();">
<br>
<input type="button" id="clear" value="Limpar" onclick="return limpar();">
2020-06-18 08:12:56 +00:00
</div>
<div class="formgroup">
<h5 class="nospacing">Estatísticas</h5>
<div><span>Traduções solicitadas: </span><span id="trad_count"></span></div>
<div><span>Traduções recebidas: </span><span id="trans_count"></span></div>
</div>
2020-06-18 08:12:56 +00:00
</form>
<hr>
</div>
<div class="loader">
<h2>Configure antes de usar</h2>
<hr>
</div>
<dl id="transcriptions">
<dt id="i1dt">fala</dt>
<dd id="i1dd"><div class="listened"></div></dd>
<dt id="o1dt">trad1</dt>
<dd id="o1dd"><div class="translated1"></div></dd>
<dt id="o2dt">trad2</dt>
<dd id="o2dd"><div class="translated2"></div></dd>
</dl>
<hr>
2020-06-21 00:17:05 +00:00
<div><span class="widthTestArea"></span></div>
2020-06-18 08:12:56 +00:00
</div>
<form id="adaptform" onsubmit="return false;">
<label for="adaptationsfield">Substituições:</label> (<span id="replacestatus">aguardando resposta</span>)
|
Carregar de arquivo: <input id="arquivo_de_substituicoes" type="file" onchange="carregar_substituicoes_de_arquivo()">
|
<a id="link_baixar_substituicoes" href="data:text/plain;plain," download="live_translator_substitutions.txt">Salvar para arquivo</a>
<textarea id="adaptationsfield" onchange="novas_substituicoes()" onkeyup="novas_substituicoes()" rows="7" style="width: 100vw; resize: none;" placeholder="||algo em qualquer contexto-->outra coisa
|idioma ou caixa|alhos-->bugalhos
||Argentina Ford Fiesta-->Argentina FurFiesta
..."></textarea>
</form>
2020-06-18 08:12:56 +00:00
</body>
</html>
`.trim();
const main_style = `
body {
font-family: sans-serif;
background-color: black;
color: white;
}
dl#transcriptions {
display: flex;
flex-wrap: wrap;
}
dl#transcriptions>dt {
display: block;
max-width: 4rem;
flex: 0 0 4rem;
border-right: 1px solid white;
border-bottom: 1px solid white;
}
dl#transcriptions>dd {
margin-inline-start: 4em;
width: calc( 100% - 9rem );
2020-06-18 08:12:56 +00:00
flex: 0 0 calc( 100% - 9rem );
overflow-y: hidden;
display: flex;
flex-direction: column-reverse;
font-weight: bold;
}
dl#transcriptions>dd>div {
width: 100%;
}
2020-06-18 08:12:56 +00:00
dl#transcriptions>dt, dl#transcriptions>dd {
align-items: baseline;
margin: 1rem;
margin-top: 0.5rem;
}
form {
display: flex;
flex-wrap: wrap;
}
.nospacing {
margin: 0px;
padding: 0px;
}
.formgroup {
padding-left: 1rem;
2020-06-20 20:42:50 +00:00
}
.formgroup:not(:last-of-type) {
2020-06-18 08:12:56 +00:00
border-right: 1px solid white;
2020-06-20 20:42:50 +00:00
padding-right: 1rem;
2020-06-18 08:12:56 +00:00
}
form input[type=button], form input[type=text], form input[type=number], form textarea {
background: black;
color: white;
border: white 0.08rem solid;
border-radius: 0.2rem;
padding: 0.2rem;
padding-left: 0.4rem;
padding-right: 0.4rem;
}
2020-06-20 20:42:50 +00:00
form input[type=text] {
2020-06-21 00:17:05 +00:00
width: 6rem;
}
form input[type=number] {
width: 3rem;
2020-06-20 20:42:50 +00:00
}
2020-06-18 08:12:56 +00:00
`.trim();
const main_script = `
window.SpeechRecognition = window.webkitSpeechRecognition || window.SpeechRecognition;
2020-06-20 19:39:22 +00:00
LANGS_1 = [null, 'pt', 'en', 'es'];
LANGS_2 = [null, 'pt-BR', 'en-US', 'es-AR'];
2020-06-18 08:12:56 +00:00
SPOKEN_LANG_POS = 0;
SELECTED_LANG1_POS = 1;
SELECTED_LANG2_POS = 2;
recognizing = false;
recognition = new Object();
recognition.continuous = false;
recognition.interimResults = true;
recognition.lang = LANGS_2[SPOKEN_LANG_POS];
recog_id = generate_random_int_id();
from_lang = LANGS_1[SPOKEN_LANG_POS];
to_lang1 = LANGS_1[SELECTED_LANG1_POS];
to_lang2 = LANGS_1[SELECTED_LANG2_POS];
loader = document.querySelector('.loader');
listened = document.querySelector('.listened');
translated1 = document.querySelector('.translated1');
translated2 = document.querySelector('.translated2');
2020-06-21 00:17:05 +00:00
widthTestArea = document.querySelector('.widthTestArea');
2020-06-18 08:12:56 +00:00
cfgform = document.querySelector('#cfgform');
cfgformp = document.querySelector('#cfgformp');
trad_count = document.querySelector('#trad_count');
trans_count = document.querySelector('#trans_count');
link_baixar_substituicoes = document.querySelector('#link_baixar_substituicoes');
arquivo_de_substituicoes = document.querySelector('#arquivo_de_substituicoes');
adaptationsfield = document.querySelector('#adaptationsfield');
replacestatus = document.querySelector('#replacestatus');
2020-06-18 08:12:56 +00:00
bg = document.querySelector('html');
recognition.onstart = function() {
recognizing = true;
console.log('onstart');
}
recognition.onerror = function(event) {
console.log('onerror', event);
aplicar();
}
recognition.onend = function() {
recognizing = false;
console.log('onend');
aplicar();
}
recognition.onsoundstart = function() {
console.log('onsoundstart');
}
recognition.onspeechstart = function() {
console.log('onspeechstart');
}
2020-06-18 08:12:56 +00:00
recognition.onresult = function(event) {
var copied = new Object();
copied.lang = from_lang;
copied.resultIndex = event.resultIndex;
copied.identity = recog_id;
copied.results = new Array();
for(var i = 0; i < event.results.length; i++) {
speech_recognition_result = new Object();
speech_recognition_result.isFinal = event.results[i].isFinal;
speech_recognition_result.alternatives = new Array();
for(var j = 0; j < event.results[i].length; j++) {
speech_recognition_alternative = new Object();
speech_recognition_alternative.transcript = event.results[i][j].transcript;
speech_recognition_alternative.confidence = event.results[i][j].confidence;
speech_recognition_result.alternatives.push(speech_recognition_alternative);
}
copied.results.push(speech_recognition_result);
}
doXhr(
'POST',
'/set/speech',
()=>{},
()=>{},
JSON.stringify(copied),
"application/json");
2020-06-18 08:12:56 +00:00
}
function aplicar_conf() {
newvalues = Object.fromEntries(
Array.from(
cfgform.querySelectorAll(':checked')
).map(x=>[x.name, parseInt(x.value)])
2020-06-20 20:42:50 +00:00
);
fontfamily = document.getElementById('ff').value;
2020-06-21 00:17:05 +00:00
fontsize = '' + document.getElementById('fs').value + 'em';
boxheight = '' + document.getElementById('bh').value + 'ex';
textcolor = '' + document.getElementById('clr').value;
2020-06-20 19:39:22 +00:00
SPOKEN_LANG_POS = newvalues['il'] || 0;
SELECTED_LANG1_POS = newvalues['1l'] || 0;
SELECTED_LANG2_POS = newvalues['2l'] || 0;
2020-06-18 08:12:56 +00:00
from_lang = LANGS_1[SPOKEN_LANG_POS] || null;
to_lang1 = LANGS_1[SELECTED_LANG1_POS] || null;
to_lang2 = LANGS_1[SELECTED_LANG2_POS] || null;
2020-06-20 20:42:50 +00:00
listened.style.fontFamily = fontfamily;
translated1.style.fontFamily = fontfamily;
translated2.style.fontFamily = fontfamily;
2020-06-21 00:17:05 +00:00
widthTestArea.style.fontFamily = fontfamily;
listened.style.fontSize = fontsize;
translated1.style.fontSize = fontsize;
translated2.style.fontSize = fontsize;
widthTestArea.style.fontSize = fontsize;
listened.style.height = boxheight;
translated1.style.height = boxheight;
translated2.style.height = boxheight;
listened.style.color = textcolor;
translated1.style.color = textcolor;
translated2.style.color = textcolor;
listened.style.display = 'flex';
translated1.style.display = 'flex';
translated2.style.display = 'flex';
listened.style.flexDirection = 'column-reverse';
translated1.style.flexDirection = 'column-reverse';
translated2.style.flexDirection = 'column-reverse';
2020-06-18 08:12:56 +00:00
if (from_lang === null) return;
recognition_old = recognition;
recognition = new SpeechRecognition();
recognition.lang = LANGS_2[SPOKEN_LANG_POS] || null;
recognition.onstart = recognition_old.onstart;
recognition.onerror = recognition_old.onerror;
recognition.onend = recognition_old.onend;
recognition.onresult = recognition_old.onresult;
recognition.continuous = recognition_old.continuous;
recognition.interimResults = recognition_old.interimResults;
recognition.onsoundstart = recognition_old.onsoundstart;
recognition.onspeechstart = recognition_old.onspeechstart;
2020-06-18 08:12:56 +00:00
recognition_old.onend = null;
recognition_old.onerror = null;
recog_id = generate_random_int_id();
}
function aplicar(){
2020-06-21 00:17:05 +00:00
loader.style.display = 'none';
2020-06-18 08:12:56 +00:00
aplicar_conf();
recognition.start();
doXhr(
'POST',
'/set/config',
()=>{},
()=>{},
JSON.stringify({
'from_lang': from_lang,
'to_lang1': to_lang1,
'to_lang2': to_lang2,
}),
"application/json");
}
function doXhr(method, URL, onsuccess, onfailure, body, bodyMime) {
var xhr = new XMLHttpRequest();
xhr.open(method, URL);
xhr.onload = function() {
if (xhr.status != 200)
onfailure(xhr);
else
onsuccess(xhr);
};
if (bodyMime) xhr.setRequestHeader("Content-Type", bodyMime);
xhr.onerror = () => onfailure(xhr);
xhr.send(body);
return xhr;
}
function generate_random_int_id() {
return Math.trunc(Math.random()*Math.pow(2, 22));
}
function check_translations() {
setTimeout(check_translations, 100);
doXhr(
'GET',
'/get/speech',
(xhr)=>{
var translations = JSON.parse(xhr.responseText);
[listened, translated1, translated2].map((el, i) => {
el.innerHTML = translations.boxes[i].split('\\n').map(escapeHtml).join('<br>');
measure_words(el);
});
trad_count.innerText = translations.translation_count;
trans_count.innerText = translations.translated_count;
2020-06-18 08:12:56 +00:00
},
()=>{});
2020-06-18 08:12:56 +00:00
}
2020-06-21 00:17:05 +00:00
function measure_words(box){
wordlist = box.innerText.split(' ').filter(x=>x.length);
wordlist = [...(new Set(wordlist.join(' ').split(' ').filter(x=>x.length)))];
wordoffsets = {};
widthTestArea.innerText = 'a a';
var a_a = widthTestArea.offsetWidth;
widthTestArea.innerText = 'aa';
var aa = widthTestArea.offsetWidth;
var _ = a_a - aa;
wordoffsets[' '] = _;
for (let word of wordlist) {
widthTestArea.innerText = word;
wordoffsets[word] = widthTestArea.offsetWidth;
}
widthTestArea.innerText = '';
measurement_summary = {};
measurement_summary.wordWidths = wordoffsets;
measurement_summary.boxWidth = box.offsetWidth;
doXhr(
'POST',
'/set/measurements',
()=>{},
()=>{},
JSON.stringify(measurement_summary),
"application/json");
}
function pausar() {
doXhr(
'POST',
'/set/togglepause',
()=>{},
()=>{});
}
function limpar() {
doXhr(
'POST',
'/set/clean',
()=>{},
()=>{});
}
var escapeHtml = (unsafe) => (unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;"));
function carregar_substituicoes_de_arquivo() {
var arq = arquivo_de_substituicoes.files[0];
arquivo_de_substituicoes.value = null;
if(arq){
var fr = new FileReader();
fr.onload = ()=>{
var text = fr.result;
adaptationsfield.value = text;
novas_substituicoes();
}
fr.readAsText(arq);
}
}
function novas_substituicoes() {
link_baixar_substituicoes.setAttribute('href', 'data:text/plain;plain,'+adaptationsfield.value);
doXhr(
'POST',
'/set/substituicoes',
(xhr)=>{
replacestatus.innerText = xhr.responseText;
},
()=>{},
JSON.stringify({text: adaptationsfield.value}),
"application/json");
}
novas_substituicoes();
2020-06-18 08:12:56 +00:00
setTimeout(check_translations, 100);
2020-06-21 00:17:05 +00:00
aplicar_conf();
2020-06-18 08:12:56 +00:00
`.trim();
var past_input_strings = [];
var latest_input_id = null;
var latest_input_string = '';
2020-06-21 00:17:05 +00:00
var assembled_input_string = '';
2020-06-18 08:12:56 +00:00
var out_strings = ['', '', ''];
var out_paragraphs = {};
2020-06-18 08:12:56 +00:00
var lang_array = ['pt', null, null];
var last_translations = {};
const silence_timeout = 4000;
2020-06-21 00:17:05 +00:00
var silence_timeout_id = null;
const translation_timeout = 1500;
2020-06-18 08:12:56 +00:00
var translator_locked = false;
2020-06-20 20:42:50 +00:00
var translation_bounced_due_to_lock = false;
const box_names = ['fala', 'trad1', 'trad2'];
const lang_names = ['pt', 'en', 'es'];
var translations_ordered = 0;
var translations_received = 0;
const translation_words_per_paragraph_max = 40;
2020-06-21 00:17:05 +00:00
const max_past_input_strings = 5;
var word_measurements = {};
var current_box_width = 0;
var pausado = false;
const measurement_error_factor = 1.2;
2020-06-24 02:21:08 +00:00
const rgx_subst = /^\|(\w*)\|(.+?)((?:-|=){2})>(.*)$/;
var substituicoes_textuais = [];
function substituir_textualmente(texto, contextos) {
let working = texto;
2020-06-24 02:21:08 +00:00
for (let substituicao_textual of substituicoes_textuais)
if (contextos.indexOf(substituicao_textual.ctx) >= 0)
working = replaceAll(
working,
new RegExp(
substituicao_textual.from,
`g${substituicao_textual.cs ? '' : 'i'}`),
substituicao_textual.to);
return working;
}
2020-06-18 08:12:56 +00:00
function update_file(location, new_content) {
return new Promise((resolve, reject) => {
var errCallback = (err) => {
if (err) reject(err);
return !!err;
};
var writeFileCallback = (err) => {
if (!errCallback(err)) resolve(true);
2020-06-24 02:22:31 +00:00
};
2020-06-18 08:12:56 +00:00
fs.exists(location, exists => {
if (!exists)
2020-06-18 08:12:56 +00:00
fs.writeFile(
location,
new_content,
undefined,
writeFileCallback);
else {
fs.readFile(location, (err, old_content) => {
if (!errCallback(err)) {
if (old_content == new_content)
2020-06-18 08:12:56 +00:00
resolve(true);
else
fs.writeFile(
location,
new_content,
undefined,
writeFileCallback);
}
});
}
});
});
}
function do_translate(text, lang_from, lang_to) {
return new Promise((resolve, reject) => {
if (!text) return resolve(text);
if (!lang_from) return resolve(text);
if (!lang_to) return resolve(text);
2020-06-18 08:12:56 +00:00
translation_token_generator.get(text).then((token) => {
return request.post(
'https://translate.google.com/translate_a/single?' +
'client=webapp&' +
`sl=${lang_from}&` +
`tl=${lang_to}&` +
`hl=${lang_from}&` +
'dt=at&' +
'dt=bd&' +
'dt=ex&' +
'dt=ld&' +
'dt=md&' +
'dt=qca&' +
'dt=rw&' +
'dt=rm&' +
'dt=ss&' +
'dt=t&' +
'otf=1&' +
'ssel=0&' +
'tsel=0&' +
'kc=1&' +
2020-06-18 08:12:56 +00:00
`${token.name}=${token.value}`,
{ form: { q: text } },
2020-06-18 08:12:56 +00:00
(error, response, body) => {
try {
body = JSON.parse(body);
} catch (e) {
2020-06-20 06:19:00 +00:00
return reject([response.statusCode, response.statusMessage, response.request.uri.href]);
2020-06-18 08:12:56 +00:00
}
resolve(get_first_while_array(body));
}
);
}, reject);
});
}
function try_to_translate() {
out_strings[0] = substituir_textualmente(
assembled_input_string,
['', box_names[0], lang_array[0]]);
out_strings[0] = out_strings[0].split('\n').splice(-4).join('\n');
out_strings[0] = out_strings[0].split('\n').flatMap(
paragraph =>
(paragraph.split(' ').length > translation_words_per_paragraph_max) ?
split_into_lines(paragraph) :
[paragraph]
).splice(-4).join('\n');
var translation_candidate = out_strings[0];
2020-06-20 20:42:50 +00:00
make_translations(translation_candidate);
}
function lock_translator_timedout() {
2020-06-18 08:12:56 +00:00
translator_locked = false;
2020-06-20 20:42:50 +00:00
if (translation_bounced_due_to_lock) {
translation_bounced_due_to_lock = false;
try_to_translate();
}
2020-06-18 08:12:56 +00:00
}
function lock_translator() {
2020-06-18 08:12:56 +00:00
translator_locked = true;
setTimeout(lock_translator_timedout, translation_timeout);
}
function prune_translations() {
for (var k in last_translations)
while (last_translations[k].length > 2048)
last_translations[k].shift();
}
2020-06-21 00:17:05 +00:00
function reset_silence_timeout() {
if (silence_timeout_id !== null)
2020-06-21 00:17:05 +00:00
clearTimeout(silence_timeout_id);
silence_timeout_id = setTimeout(silence_timeout_exceeded_action, silence_timeout);
}
function silence_timeout_exceeded_action() {
if (pausado)
return reset_silence_timeout();
if (silence_timeout_id !== null)
clearTimeout(silence_timeout_id);
2020-06-21 00:17:05 +00:00
past_input_strings = [];
latest_input_string = '';
out_strings[0] = '';
setTimeout(() => {
out_strings[1] = '';
out_strings[2] = '';
}, 1150);
2020-06-21 00:17:05 +00:00
}
2020-06-18 08:12:56 +00:00
function make_translations(text) {
prune_translations();
var translations_to_get = [];
for (let i = 1; i < lang_array.length; i++) {
if (lang_array[i] == null)
2020-06-18 08:12:56 +00:00
out_strings[i] = '';
else if (lang_array[i] == lang_array[0])
2020-06-18 08:12:56 +00:00
out_strings[i] = out_strings[0];
else if (translations_to_get.indexOf(lang_array[i]) == -1)
2020-06-18 08:12:56 +00:00
translations_to_get.push(lang_array[i]);
}
if (!translator_locked) {
lock_translator();
let paragraphs = text.split('\n');
let this_translations = {};
for (let translang of translations_to_get)
this_translations[translang] = paragraphs.map((x, i) =>
(out_paragraphs[translang] || [])[i] || '···'
);
out_paragraphs = this_translations;
for (let paragraph_index in paragraphs) {
let paragraph = paragraphs[paragraph_index];
for (let translang of translations_to_get) {
last_translations[translang] = last_translations[translang] || [];
cached = Object.fromEntries(last_translations[translang])[paragraph];
if (cached)
this_translations[translang][paragraph_index] = cached;
else {
translations_ordered += 1;
do_translate(paragraph, lang_array[0], translang).then((translation) => {
last_translations[translang].push([paragraph, translation]);
this_translations[translang][paragraph_index] = translation;
update_displayed_translations_for_lang(translang);
translations_received += 1;
}, err => console.log(`Erro ao traduzir para '${translang}': ${err}`));
2020-06-18 08:12:56 +00:00
}
}
}
for (let i = 1; i < lang_array.length; i++)
if (lang_array[i] == lang_array[0])
out_paragraphs[lang_array[i]] = paragraphs;
let ps = paragraphs.length;
if (ps >= 2)
for (let translang of translations_to_get)
if (this_translations[translang][ps - 2] === this_translations[translang][ps - 1])
this_translations[translang][ps - 1] = '···';
update_displayed_translations_all_langs();
2020-06-20 20:42:50 +00:00
} else
translation_bounced_due_to_lock = true;
2020-06-18 08:12:56 +00:00
}
function update_displayed_translations_for_lang(translang) {
for (let i = 1; i < lang_array.length; i++)
if (lang_array[i] == translang)
out_strings[i] = substituir_textualmente(
out_paragraphs[translang].join('\n'),
['', lang_array[i], box_names[i]]
);
}
function update_displayed_translations_all_langs() {
for (let i = 1; i < lang_array.length; i++)
out_strings[i] = substituir_textualmente(
(out_paragraphs[lang_array[i]] || []).join('\n'),
['', lang_array[i], box_names[i]]
);
}
2020-06-18 08:12:56 +00:00
function get_first_while_array(a) {
if (Array.isArray(a))
return get_first_while_array(a[0]);
else
return a;
}
function pause_toggle() {
pausado = !pausado;
}
2020-06-18 08:12:56 +00:00
server.get("/", (req, res) => {
res.writeHead(200, "OK", { 'Content-Type': 'text/html' });
2020-06-18 08:12:56 +00:00
res.write(index_page);
res.end();
});
server.get("/main.css", (req, res) => {
res.writeHead(200, "OK", { 'Content-Type': 'text/css' });
2020-06-18 08:12:56 +00:00
res.write(main_style);
res.end();
});
server.get("/main.js", (req, res) => {
res.writeHead(200, "OK", { 'Content-Type': 'application/javascript' });
2020-06-18 08:12:56 +00:00
res.write(main_script);
res.end();
});
server.post("/set/config", (req, res) => {
lang_array[1] = req.body.to_lang1;
lang_array[2] = req.body.to_lang2;
res.end();
});
server.get("/get/speech", (req, res) => {
res.writeHead(200, "OK", { 'Content-Type': 'application/json' });
res.write(JSON.stringify({
boxes: out_strings,
translation_count: translations_ordered,
translated_count: translations_received,
}));
2020-06-18 08:12:56 +00:00
res.end();
for (let i in lang_array) {
update_file(
'live_translator_caixa_' + box_names[i] + '.txt',
2020-06-18 08:12:56 +00:00
out_strings[i]);
}
2020-06-21 00:17:05 +00:00
for (let lang of [...(new Set(lang_array))]) {
2020-06-18 08:12:56 +00:00
let i = lang_array.indexOf(lang);
if (lang_array[i] === null) continue;
2020-06-18 08:12:56 +00:00
update_file(
'live_translator_idioma_' + lang + '.txt',
2020-06-18 08:12:56 +00:00
out_strings[i]);
}
2020-06-21 00:17:05 +00:00
for (let i in lang_array) {
update_file(
'live_translator_3linhas_caixa_' + box_names[i] + '.txt',
2020-06-21 00:17:05 +00:00
split_into_lines(out_strings[i]).splice(-3).join('\r\n'));
}
for (let i in lang_array) {
update_file(
'live_translator_2linhas_caixa_' + box_names[i] + '.txt',
2020-06-21 00:17:05 +00:00
split_into_lines(out_strings[i]).splice(-2).join('\r\n'));
}
for (let lang of [...(new Set(lang_array))]) {
let i = lang_array.indexOf(lang);
if (lang_array[i] === null) continue;
2020-06-21 00:17:05 +00:00
update_file(
'live_translator_3linhas_idioma_' + lang + '.txt',
2020-06-21 00:17:05 +00:00
split_into_lines(out_strings[i]).splice(-3).join('\r\n'));
}
for (let lang of [...(new Set(lang_array))]) {
let i = lang_array.indexOf(lang);
if (lang_array[i] === null) continue;
2020-06-21 00:17:05 +00:00
update_file(
'live_translator_2linhas_idioma_' + lang + '.txt',
2020-06-21 00:17:05 +00:00
split_into_lines(out_strings[i]).splice(-2).join('\r\n'));
}
2020-06-18 08:12:56 +00:00
});
server.post("/set/speech", (req, res) => {
var speech = req.body;
res.end();
if (!pausado) {
if (speech.lang != lang_array[0]) {
lang_array[0] = speech.lang;
last_translations = {};
out_strings[0] = '';
assembled_input_string = '';
past_input_strings = [];
latest_input_string = '';
latest_input_id = null;
}
if (speech.identity != latest_input_id) {
latest_input_id = speech.identity;
past_input_strings.push(latest_input_string);
if (past_input_strings.length > max_past_input_strings)
past_input_strings.shift();
latest_input_string = '';
}
var s = '';
for (var i = speech.resultIndex; i < speech.results.length; i++)
s += ' ' + speech.results[i].alternatives[0].transcript;
s = s.split(' ').filter(x => x.length).join(' ');
latest_input_string = s;
reset_silence_timeout();
assembled_input_string = (
past_input_strings.join('\n') + '\n' + latest_input_string
).split('\n').map(paragraph =>
paragraph.split(' ').filter(x => x.length).join(' ')
).filter(x => x.length).join('\n');
try_to_translate();
2020-06-18 08:12:56 +00:00
}
});
2020-06-21 00:17:05 +00:00
function split_into_lines(text) {
var paragraphs = text.split('\n').map(p => p.split(' ').flatMap(x => [' ', x]).slice(1));
2020-06-21 00:17:05 +00:00
var lines = [];
for (let tokens of paragraphs) {
var line = [];
for (let token of tokens) {
if ((token == ' ') && (line.length == 0))
continue;
let sizemapper = (wrd) => word_measurements[wrd] || 0;
let tokensize = sizemapper(token);
let linesize = line.map(sizemapper).reduce((a, b) => a + b, 0);
if (linesize + tokensize > current_box_width / measurement_error_factor) {
lines.push(line);
line = [];
}
line.push(token);
2020-06-21 00:17:05 +00:00
}
lines.push(line);
2020-06-21 00:17:05 +00:00
}
for (let lnum in lines)
lines[lnum] = lines[lnum].join('').trim(' ');
lines = lines.filter(x => x.length);
2020-06-21 00:17:05 +00:00
return lines;
}
server.post("/set/measurements", (req, res) => {
var measurements = req.body;
res.end();
for (let word in measurements.wordWidths)
word_measurements[word] = measurements.wordWidths[word];
if (measurements.boxWidth)
current_box_width = measurements.boxWidth;
});
server.post("/set/togglepause", (req, res) => {
res.end();
pause_toggle();
});
server.post("/set/clean", (req, res) => {
res.end();
let op = pausado;
pausado = false;
silence_timeout_exceeded_action();
pausado = op;
});
server.post("/set/substituicoes", (req, res) => {
let substituicoes = req.body.text.trim().replace('\r\n', '\n').split('\n');
if (substituicoes.length == 1 && substituicoes[0] == '')
substituicoes.pop();
let ctxs = ['', ...box_names, ...lang_names];
let cont_sucessos = 0;
let cont_erros = 0;
let erros_linhas = [];
let sucessos = [];
for (let i in substituicoes) {
let linha = substituicoes[i];
let match = linha.match(rgx_subst);
if (match != null && ctxs.indexOf(match[1]) >= 0) {
let matched = {
ctx: match[1],
from: match[2],
2020-06-24 02:21:08 +00:00
to: match[4],
cs: match[3] == '==',
};
2020-06-24 02:21:08 +00:00
// console.log(match);
// console.log(matched);
cont_sucessos += 1;
sucessos.push(matched);
} else {
cont_erros += 1;
erros_linhas.push(parseInt(i) + 1);
}
}
res.write(
`Sucessos: ${cont_sucessos}; ` +
`Erros: ${cont_erros}` +
((cont_erros > 0) ? (
`, nas linhas [${erros_linhas.map(x => x.toString()).join(', ')}]`
) : ''));
res.end();
substituicoes_textuais = sucessos;
});
server.listen(13653, '127.0.0.1', () => {
console.log('Abra http://localhost:13653 no Chrome.');
});
2020-06-20 06:19:00 +00:00