2020-02-24 15:05:18 +01:00
|
|
|
const clone = require('lodash/cloneDeep');
|
|
|
|
|
|
|
|
exports.translateComments = (text, lang, dict, codeLang) => {
|
|
|
|
const knownComments = Object.keys(dict);
|
|
|
|
const config = { knownComments, dict, lang };
|
|
|
|
switch (codeLang) {
|
|
|
|
case 'js':
|
|
|
|
return transMultiline(transInline(text, config), config);
|
|
|
|
case 'jsx':
|
|
|
|
return transJSX(text, config);
|
|
|
|
case 'html':
|
|
|
|
return transHTML(transCSS(text, config), config);
|
|
|
|
default:
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.translateCommentsInChallenge = (challenge, lang, dict, codeLang) => {
|
|
|
|
const challClone = clone(challenge);
|
|
|
|
|
|
|
|
if (challClone.files[0] && challClone.files[0].contents) {
|
|
|
|
challClone.files[0].contents = this.translateComments(
|
|
|
|
challenge.files[0].contents,
|
|
|
|
lang,
|
|
|
|
dict,
|
|
|
|
codeLang
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return challClone;
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.mergeChallenges = (engChal, transChal) => {
|
2020-08-13 12:25:53 +02:00
|
|
|
const hasTests =
|
|
|
|
(engChal.tests && transChal.tests) ||
|
|
|
|
(engChal.question && transChal.question);
|
2020-03-05 14:13:56 +01:00
|
|
|
const challenge = {
|
2020-02-24 15:05:18 +01:00
|
|
|
...engChal,
|
|
|
|
description: transChal.description,
|
|
|
|
instructions: transChal.instructions,
|
|
|
|
localeTitle: transChal.localeTitle,
|
2020-08-13 12:25:53 +02:00
|
|
|
forumTopicId: transChal.forumTopicId
|
2020-02-24 15:05:18 +01:00
|
|
|
};
|
2020-08-13 12:25:53 +02:00
|
|
|
if (!hasTests)
|
|
|
|
throw Error(
|
|
|
|
`Both challenges must have tests or questions.
|
|
|
|
title: ${engChal.title}
|
|
|
|
localeTitle: ${transChal.localeTitle}`
|
|
|
|
);
|
|
|
|
// TODO: this should break the build when we go to production, but
|
|
|
|
// not for testing.
|
|
|
|
if (transChal.tests && transChal.tests.length !== engChal.tests.length) {
|
|
|
|
console.error(
|
|
|
|
`Challenges in both languages must have the same number of tests.
|
|
|
|
title: ${engChal.title}
|
|
|
|
localeTitle: ${transChal.localeTitle}`
|
|
|
|
);
|
|
|
|
return challenge;
|
|
|
|
}
|
|
|
|
|
|
|
|
// throw Error(
|
|
|
|
// `Challenges in both languages must have the same number of tests.
|
|
|
|
// title: ${engChal.title}
|
|
|
|
// localeTitle: ${transChal.localeTitle}`
|
|
|
|
// );
|
|
|
|
|
|
|
|
if (transChal.tests) {
|
|
|
|
const translatedTests =
|
|
|
|
engChal.challengeType === 7
|
|
|
|
? transChal.tests.map(({ title }, i) => ({
|
|
|
|
title,
|
|
|
|
id: engChal.tests[i].id
|
|
|
|
}))
|
|
|
|
: transChal.tests.map(({ text }, i) => ({
|
|
|
|
text,
|
|
|
|
testString: engChal.tests[i].testString
|
|
|
|
}));
|
|
|
|
challenge.tests = translatedTests;
|
|
|
|
} else {
|
|
|
|
challenge.question = transChal.question;
|
|
|
|
}
|
|
|
|
|
2020-03-05 14:13:56 +01:00
|
|
|
// certificates do not have forumTopicIds
|
|
|
|
if (challenge.challengeType === 7) delete challenge.forumTopicId;
|
|
|
|
return challenge;
|
2020-02-24 15:05:18 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
// bare urls could be interpreted as comments, so we have to lookbehind for
|
|
|
|
// http:// or https://
|
|
|
|
function transInline(text, config) {
|
|
|
|
return translateGeneric(
|
|
|
|
text,
|
|
|
|
config,
|
|
|
|
'(^[^\'"`]*?(?<!https?:)//\\s*)',
|
|
|
|
'(\\s*$)'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function transMultiline(text, config) {
|
|
|
|
return translateGeneric(text, config, '(/\\*\\s*)', '(\\s*\\*/)');
|
|
|
|
}
|
|
|
|
|
|
|
|
// CSS has to be handled separately since it is looking for comments inside tags
|
|
|
|
function transCSS(text, config) {
|
|
|
|
const regex = /<style>.*?<\/style>/gms;
|
|
|
|
const matches = text.matchAll(regex);
|
|
|
|
|
|
|
|
for (const [match] of matches) {
|
|
|
|
text = text.replace(match, transMultiline(match, config));
|
|
|
|
}
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
|
|
|
function transJSX(text, config) {
|
|
|
|
return translateGeneric(text, config, '({[^}]*/\\*\\s*)', '(\\s*\\*/[^{]*})');
|
|
|
|
}
|
|
|
|
|
|
|
|
function transHTML(text, config) {
|
|
|
|
return translateGeneric(text, config, '(<!--\\s*)', '(\\s*-->)');
|
|
|
|
}
|
|
|
|
|
|
|
|
function translateGeneric(text, config, regexBefore, regexAfter) {
|
|
|
|
const { knownComments, dict, lang } = config;
|
|
|
|
const regex = new RegExp(regexBefore + '(.*?)' + regexAfter, 'gms');
|
|
|
|
const matches = text.matchAll(regex);
|
|
|
|
|
|
|
|
for (const [match, before, comment, after] of matches) {
|
|
|
|
if (knownComments.includes(comment)) {
|
|
|
|
text = text.replace(match, `${before}${dict[comment][lang]}${after}`);
|
|
|
|
} else {
|
|
|
|
console.warn(`${comment} does not appear in the comment dictionary`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return text;
|
|
|
|
}
|