Skip to content

Commit

Permalink
Merge pull request #426 from openstad/feature/deepl-translation-widge…
Browse files Browse the repository at this point in the history
…t-in-menu

Feature/deepl translation widget in menu
  • Loading branch information
rudivanhierden committed Mar 12, 2024
2 parents f39deda + 54cf429 commit 6816568
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 172 deletions.
61 changes: 60 additions & 1 deletion packages/cms/lib/modules/openstad-global/index.js
Expand Up @@ -9,12 +9,69 @@ const fs = require('fs');
const fields = require('./lib/fields');
const arrangeFields = require('./lib/arrangeFields');

const deepl = require('deepl-node');
const cache = require('../../../services/cache').cache;
const cacheLanguagesLifespan = (24 * 60 * 60) * 7; // set lifespan of language cache to a week;
const translatorConfig = { maxRetries: 5, minTimeout: 10000 };

function unauthorized(req, res) {
var challengeString = 'Basic realm=Openstad';
res.set('WWW-Authenticate', challengeString);
return res.status(401).send('Authentication required.');
}

async function getSupportedLanguages(deeplAuthKey) {
let supportedLanguages = [];
const cacheKeyForLanguages = 'translationLanguages'

if (cache.get(cacheKeyForLanguages)) {
console.log("Received languages from cache");
supportedLanguages = cache.get(cacheKeyForLanguages);
}
else if (deeplAuthKey) {
console.log({deeplAuthKey})
try {
const translator = new deepl.Translator(deeplAuthKey, translatorConfig);
await translator.getTargetLanguages().then(response => {
supportedLanguages = response;
});

// convert items to their own language
const languageTranslatedCollection = [];

for (const language of supportedLanguages) {
languageTranslatedCollection.push(
translator.translateText(
language.name,
'EN',
language.code
)
);
}

await Promise.all(languageTranslatedCollection).then(languages => {
supportedLanguages = languages.map((language, index) => {
language['code'] = supportedLanguages[index].code;
return language;
});

cache.set(`${cacheKeyForLanguages}`, supportedLanguages, {
life: cacheLanguagesLifespan
});
});
} catch(error) {
supportedLanguages = supportedLanguages.map((language, index) => {
language['text'] = supportedLanguages[index].name;
return language;
})
console.error({translationError: error});
}
} else {
console.error({translationError: "Could not fetch languages for the translation widget: Key not set"});
}
console.log("deeplAuthKey")
return supportedLanguages;
}

module.exports = {
improve: 'apostrophe-global',
Expand All @@ -40,8 +97,10 @@ module.exports = {

options.arrangeFields = arrangeFields.concat(options.arrangeFields || []);

self.apos.app.use((req, res, next) => {
self.apos.app.use(async (req, res, next) => {
let deeplAuthKey = options.deeplKey;
req.data.global = req.data.global ? req.data.global : {};
req.data.global.languages = await getSupportedLanguages(deeplAuthKey);
return next();
});

Expand Down
Expand Up @@ -43,7 +43,7 @@ module.exports = [
{
name: 'mainMenu',
label: 'Hoofdmenu',
fields: ['arrangeMainMenu', 'displayLoginTopLink', 'mainMenuItems', 'ctaButtonText', 'ctaButtonUrl', 'topLinks', 'displayMyAcount', "myAccountButtonText", 'shouldAutoTranslate']
fields: ['arrangeMainMenu', 'displayLoginTopLink', 'mainMenuItems', 'ctaButtonText', 'ctaButtonUrl', 'topLinks', 'displayMyAcount', "myAccountButtonText", 'translateInMenu']
},
{
name: 'userRights',
Expand Down
4 changes: 2 additions & 2 deletions packages/cms/lib/modules/openstad-global/lib/fields.js
Expand Up @@ -91,8 +91,8 @@ module.exports = [
},
{
type: 'boolean',
name: 'shouldAutoTranslate',
label: 'Try to translate each page on entering',
name: 'translateInMenu',
label: 'The translate widget should be visible in the menubar',
def: false,
choices: [
{
Expand Down
118 changes: 87 additions & 31 deletions packages/cms/lib/modules/openstad-global/public/js/always.js
@@ -1,60 +1,104 @@
apos.on('ready', function () {
var firstTimeLoading = true;
var nodes = [];
var selectedLanguage = localStorage.getItem("targetLanguageCode");

/**
* The translate widget if set on the page will trigger an onchange event when it has been loaded
* thus triggering the fetching of translations. Then this one should do nothing and let the dedicated
* widget make the call.
*/
var translationWidgetOnSamePage = $('.translation-widget-select').length > 0;

var userHasSpecialRole = hasModeratorRights; // references global var specified in layout.js';
var shouldTranslate = shouldTranslateOnEveryPage; // references global var specified in layout.js';
var toastContainer = document.querySelector("#openstad-toast");

if(shouldTranslate) {
if (!userHasSpecialRole && !translationWidgetOnSamePage && selectedLanguage && selectedLanguage !== 'nl') {
addToast(toastContainer, "info", "De pagina wordt vertaald...", 3000);
var nlContents = [];

nodes = handleNode(document.body, nodes);
var nlContents = nodes.map(function (itemToTranslate) { return itemToTranslate.orgText });
var languageSelectContainer = $('.language-select-container');

$('#translation-widget-select-global')
.on('change', function (e) {
changeLanguage(e);
});

function setSelectDisabled(select) {
select.setAttribute('disabled', true);
languageSelectContainer.addClass('languageLoading');
};

function setSelectEnabled(select) {
select.removeAttribute('disabled');
languageSelectContainer.removeClass('languageLoading');
};

function saveLanguagePreference(targetLanguageCode) {
try{
localStorage.setItem("targetLanguageCode", targetLanguageCode);
} catch(quotaExceededError) {
console.log("Could not save the language preference");
}
}



function changeLanguage (e) {
var select = e.target;
var targetLanguageCode = select.value;
setSelectDisabled(select);
var node = document.body;

if (firstTimeLoading) {
nodes = handleNode(node, nodes);
nlContents = nodes.map(function(itemToTranslate) { return itemToTranslate.orgText });
firstTimeLoading = false;
}

if (targetLanguageCode === 'nl') {
console.log("Language is set to the default: " + targetLanguageCode +". No need to translate");
changeTextInNodes(nlContents, nodes);
setSelectEnabled(select);
saveLanguagePreference(targetLanguageCode);
syncOtherTranslationWidgets(targetLanguageCode);
} else {
console.log('translating to', targetLanguageCode);

var toastContainer = document.querySelector("#openstad-toast");
addToast(toastContainer, "info", "De pagina wordt vertaald...", 5000);

$.ajax({
url: '/modules/openstad-global/translate',
method: 'post',
url: '/modules/translation-widgets/submit',
method: 'POST',
contentType: "application/json",
data: JSON.stringify({
contents: nlContents,
sourceLanguageCode: 'nl',
targetLanguageCode: selectedLanguage,
targetLanguageCode: targetLanguageCode,
origin: window.location.href
}),
success: function (sentences) {
sentences = sentences.map(function (sentence) { return sentence.text });
saveLanguagePreference(targetLanguageCode);
sentences = sentences.map(function(sentence) { return sentence.text });
changeTextInNodes(sentences, nodes);
setSelectEnabled(select);
addToast(toastContainer, "success", "De pagina is succesvol vertaald");
},
syncOtherTranslationWidgets(targetLanguageCode);
},
error: function() {
setSelectEnabled(select);
// setSelectedLanguage('nl');
addToast(toastContainer, "error", "De pagina kon niet worden vertaald");
}
});
} else if(userHasSpecialRole) {
if(!sessionStorage.getItem("cannot-translate-acknowledged")) {
addToast(toastContainer, "info", "De vertaalwidget kan niet worden gebruikt tijdens het bewerken van de site.", 0, function() {
sessionStorage.setItem("cannot-translate-acknowledged", true);
});
}
}
};


var select = document.querySelector('#translation-widget-select-global');
var isNormalUser = !hasModeratorRights; // references global var specified in layout.js
if(isNormalUser) {
setSelectedLanguage(localStorage.getItem('targetLanguageCode'));
} else if(select) {
select.setAttribute('disabled', true);
}
});

changeTextInNodes = function (sentences, nodes) {

function changeTextInNodes (sentences, nodes) {
sentences.forEach(function (sentence, index) {
nodes[index].node.textContent = sentence;
});
}

handleNode = function (node, toBeTranslated) {
function handleNode (node, toBeTranslated) {
var childNodes = node.childNodes;
for (var i = 0; i < childNodes.length; i++) {
if (childNodes[i].nodeType == Node.ELEMENT_NODE) {
Expand Down Expand Up @@ -118,4 +162,16 @@ function addToast(container, typeOfInfoErrorOrSuccess, text, timeout, onClick) {
}, timeout);
}
}


}

function setSelectedLanguage(language) {
$('#translation-widget-select-global').val(language ? language : 'nl').trigger('change');
}

function syncOtherTranslationWidgets(language) {
document.querySelectorAll("#translation-widget-select").forEach(function(select) {
select.value = language ? language : 'nl';
});
}
52 changes: 1 addition & 51 deletions packages/cms/lib/modules/translation-widgets/index.js
Expand Up @@ -13,9 +13,6 @@ module.exports = {
],
playerData: [],
construct: function (self, options) {
let deeplAuthKey = options.deeplKey;
let supportedLanguages = [];

options.arrangeFields = (options.arrangeFields || []).concat([
{
name: 'generalGroup',
Expand All @@ -31,61 +28,14 @@ module.exports = {

const superLoad = self.load;
self.load = async (req, widgets, callback) => {
const cacheKeyForLanguages = 'translationLanguages'

if (cache.get(cacheKeyForLanguages)) {
console.log("Received languages from cache");
supportedLanguages = cache.get(cacheKeyForLanguages);
}
else if (deeplAuthKey) {
try {
const translator = new deepl.Translator(deeplAuthKey, translatorConfig);
await translator.getTargetLanguages().then(response => {
supportedLanguages = response;
});

// convert items to their own language
const languageTranslatedCollection = [];

for (const language of supportedLanguages) {
languageTranslatedCollection.push(
translator.translateText(
language.name,
'EN',
language.code
)
);
}

await Promise.all(languageTranslatedCollection).then(languages => {
supportedLanguages = languages.map((language, index) => {
language['code'] = supportedLanguages[index].code;
return language;
});

cache.set(`${cacheKeyForLanguages}`, supportedLanguages, {
life: cacheLanguagesLifespan
});
});
} catch(error) {
supportedLanguages = supportedLanguages.map((language, index) => {
language['text'] = supportedLanguages[index].name;
return language;
})
console.error({translationError: error});
}
} else {
console.error({translationError: "Could not fetch languages for the translation widget: Key not set"});
}

widgets.forEach((widget) => {
if (widget.containerStyles) {
const containerId = self.apos.utils.generateId();
widget.containerId = containerId;
widget.formattedContainerStyles = styleSchema.format(containerId, widget.containerStyles);
}
widget.cssHelperClassesString = widget.cssHelperClasses ? widget.cssHelperClasses.join(' ') : '';
widget.supportedLanguages = supportedLanguages;
widget.supportedLanguages = req.data.global.languages;
});

return superLoad(req, widgets, callback);
Expand Down
Expand Up @@ -38,6 +38,13 @@
background-position:right;
}


.translation-widget-select.compact {
padding: 0rem;
width: auto;
background-image: none;
}

.translation-widget-select:disabled {
opacity: .5;
}
Expand Down

1 comment on commit 6816568

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Published new image: openstad/frontend:development-6816568

Please sign in to comment.