commit 0f5224c7fd9d4a73760a27088d0e6199659e8f90 Author: OverPoweredDev Date: Mon Aug 2 13:35:02 2021 +0530 added feature: context translation possible improvements but works well the way it is diff --git a/src/content/content.css b/src/content/content.css index b926777..a03eb85 100644 --- a/src/content/content.css +++ b/src/content/content.css @@ -1,10 +1,12 @@ -[data-translation] { +/* With major help from https://stackoverflow.com/a/25836471/14131011 for tooltip styling */ + +[data-translation]:not([data-translation=""]) { display: inline-block; position: relative; } /* Tooltip styling */ -[data-translation]:before { +[data-translation]:not([data-translation=""]):before { content: attr(data-translation); display: none; position: absolute; @@ -14,13 +16,13 @@ font-size: 14px; line-height: 1.4; min-width: 5em; + max-width: 10em; text-align: center; border-radius: 4px; } /* Dynamic horizontal centering */ -[data-translation-position="top"]:before, -[data-translation-position="bottom"]:before { +[data-translation]:not([data-translation=""]):before { left: 50%; -ms-transform: translateX(-50%); -moz-transform: translateX(-50%); @@ -28,38 +30,13 @@ transform: translateX(-50%); } -/* Dynamic vertical centering */ -[data-translation-position="right"]:before, -[data-translation-position="left"]:before { - top: 50%; - -ms-transform: translateY(-50%); - -moz-transform: translateY(-50%); - -webkit-transform: translateY(-50%); - transform: translateY(-50%); -} - -[data-translation-position="top"]:before { +[data-translation]:not([data-translation=""]):before { bottom: 100%; margin-bottom: 6px; } -[data-translation-position="right"]:before { - left: 100%; - margin-left: 6px; -} - -[data-translation-position="bottom"]:before { - top: 100%; - margin-top: 6px; -} - -[data-translation-position="left"]:before { - right: 100%; - margin-right: 6px; -} - /* Tooltip arrow styling/placement */ -[data-translation]:after { +[data-translation]:not([data-translation=""]):after { content: ''; display: none; position: absolute; @@ -70,52 +47,23 @@ } /* Dynamic horizontal centering for the tooltip */ -[data-translation-position="top"]:after, -[data-translation-position="bottom"]:after { +[data-translation]:not([data-translation=""]):after{ left: 50%; margin-left: -6px; -} - -/* Dynamic vertical centering for the tooltip */ -[data-translation-position="right"]:after, -[data-translation-position="left"]:after { - top: 50%; - margin-top: -6px; -} - -[data-translation-position="top"]:after { bottom: 100%; border-width: 6px 6px 0; border-top-color: #000; } -[data-translation-position="right"]:after { - left: 100%; - border-width: 6px 6px 6px 0; - border-right-color: #000; -} - -[data-translation-position="bottom"]:after { - top: 100%; - border-width: 0 6px 6px; - border-bottom-color: #000; -} - -[data-translation-position="left"]:after { - right: 100%; - border-width: 6px 0 6px 6px; - border-left-color: #000; -} - /* Show the tooltip when hovering */ -[data-translation]:hover:before, -[data-translation]:hover:after { +[data-translation]:not([data-translation=""]):hover:before, +[data-translation]:not([data-translation=""]):hover:after { display: block; z-index: 50; } -[data-translation]:hover{ - animation-delay: 100ms; - -moz-animation-delay: 100ms; - -webkit-animation-delay: 100ms; +[data-translation]:not([data-translation=""]):hover{ + animation-delay: 500ms; + -moz-animation-delay: 500ms; + -webkit-animation-delay: 500ms; } \ No newline at end of file diff --git a/src/lib/translate.js b/src/lib/translate.js index c7a3e53..180dac9 100644 --- a/src/lib/translate.js +++ b/src/lib/translate.js @@ -4,21 +4,36 @@ function addHoverTag(targetLanguage, sourceLanguage) { // - ([A-z0-9']+) apart from elements, we consider all words, numbers or words containing "'" $('p').each(function () { - $(this).html($(this).html().replace(/(?![^<]*?>)([A-z0-9']+)/g , '$1')); + $(this).html($(this).html().replace(/(?![^<]*?>)([A-z0-9']+)/g, '$1')); }); - let setTimeoutConst; - $("hover").hover(function() { + $("hover").hover(async function () { let self = $(this) - setTimeoutConst = setTimeout(async function () { - let text = self.attr('data-translation'); - if(self.text() === text) { - let translation = await translateWord(text, sourceLanguage, targetLanguage); - self.attr('data-translation', translation); + let preTranslation = self.attr('data-translation'); // basically just the data before translating it (possibly again) + let original = self.attr('data-original'); + + if (original === preTranslation) { + let parent = self.parent(); + let contextTranslation = await translateWord(parent.html(), sourceLanguage, targetLanguage).then((string) => { + let parser = new DOMParser(); + return parser.parseFromString(string, 'text/html').body + }); + + // if (contextTranslation.children.length !== parent.children().length) { + // console.log(contextTranslation.children, parent.children()) + // console.log(contextTranslation.innerText, parent.text()) + // } + + for (let element of parent.children()) { + let original = element.getAttribute('data-original'); + let translation = contextTranslation.querySelector('[data-original="' + original + '"]'); + translation.remove(); + + element.setAttribute('data-translation', translation.textContent); } - }, 1000); - }, function() { - clearTimeout(setTimeoutConst); + + // self.attr('data-translation', translation); + } }); } @@ -39,59 +54,59 @@ async function translateWord(inputText, sourceLanguage, targetLanguage) { } async function translateWebpage(sourceLanguage, targetLanguage) { - let textElements = getTextElements(); + function getTextElements() { + let textElements = []; - let transportDocument = createNewDocument(textElements); - let translatedDocument = await getTranslatedDocument(sourceLanguage, targetLanguage, transportDocument, 'transport.html'); + // on passing the entire body it just returns the body again so I'm passing the children individually + $('body').children().each(function () { + getBlockNodes($(this)[0], textElements); + }); - let translatedElements = splitText(translatedDocument); - replaceText(translatedElements); -} + return [...new Set(textElements)]; + } -function getTextElements() { - let textElements = []; + function replaceText(translatedElements) { + $('[data-replace-id]').each(function () { + let id = $(this).attr('data-replace-id'); + $(this).html(translatedElements[id]); + }) + } - // on passing the entire body it just returns the body again so I'm passing the children individually - $('body').children().each(function () { - getBlockNodes($(this)[0], textElements); - }); + function splitText(translatedDocument) { + let list = []; - return [...new Set(textElements)]; -} + let d = new DOMParser(); + let document = d.parseFromString(translatedDocument, 'text/html'); + let nodeList = document.querySelectorAll('[data-id]'); -function replaceText(translatedElements) { - $('[data-replace-id]').each(function () { - let id = $(this).attr('data-replace-id'); - $(this).html(translatedElements[id]); - }) -} + nodeList.forEach((element) => { + list.push(element.innerHTML); + }) -function splitText(translatedDocument) { - let list = []; + return list; + } - let d = new DOMParser(); - let document = d.parseFromString(translatedDocument, 'text/html'); - let nodeList = document.querySelectorAll('[data-id]'); + function createNewDocument(nodeList) { + let data = ""; - nodeList.forEach((element) => { - list.push(element.innerHTML); - }) + nodeList.forEach((node, index) => { + data += "
" + data += node; + data += "
"; + }); - return list; -} + data += ""; -function createNewDocument(nodeList) { - let data = ""; + return new Blob([data], {type: 'text/plain'}); + } - nodeList.forEach((node, index) => { - data += "
" - data += node; - data += "
"; - }); + let textElements = getTextElements(); - data += ""; + let transportDocument = createNewDocument(textElements); + let translatedDocument = await getTranslatedDocument(sourceLanguage, targetLanguage, transportDocument, 'transport.html'); - return new Blob([data], {type: 'text/plain'}); + let translatedElements = splitText(translatedDocument); + replaceText(translatedElements); } // purely for test purposes