(function () {
'use strict';
const ts = Date.now() + (29 * 24 * 60 * 60 * 1000);
const pageTitle = mw.config.get('wgPageName').replace(/_/g, ' ');
function createInputGUI(contributors) {
var gui = document.createElement('div');
gui.style.position = 'fixed';
gui.style.top = '50%';
gui.style.left = '50%';
gui.style.transform = 'translate(-50%, -50%)';
gui.style.backgroundColor = '#f8f9fa';
gui.style.border = '1px solid #a2a9b1';
gui.style.padding = '20px';
gui.style.zIndex = 10000;
gui.style.overflowY = 'auto';
gui.style.maxHeight = '80vh';
gui.innerHTML = `
<label for="langCode" style="font-weight:bold">Language code</label>
<p style="font-size: 50%;">The two-letter language code of the Wikipedia that this article was translated from.</p>
<input type="text" id="langCode" placeholder="e.g. zh, es, fr"><br><br>
<label for="articleName" style="font-weight:bold">Article name</label>
<p style="font-size: 50%;">The name of the page on the other Wikipedia that this article was translated from.</p>
<input type="text" id="articleName" placeholder="e.g. 维基百科"><br><br>
<label for="addTranslatedPageTemplate" style="font-weight:bold">
<input checked type="checkbox" id="addTranslatedPageTemplate"> Add {{Translated page}} to talk page
</label>
<p style="font-size: 50%;margin-bottom:1rem;">Add <a href="/wiki/Template:Translated page">{{Translated page}}</a> to the talk page <a href="/wiki/Help:Translation#License requirements">if</a> the translated content is significant.</p>
<label style="font-weight:bold">Warn users</label>
<p style="font-size: 50%;">Send a notice to the user(s) who added the non-attributed text.</p>
<div id="contributorsContainer">
<div id="contributorsList"></div>
</div><br>
<button id="submitTranslation">Submit</button>
<button id="closeGUI">Close</button>
<div id="logContainer" style="display: none; margin-top: 20px; border-top: 1px solid #a2a9b1; padding-top: 10px;">
<h3>Log</h3>
<ul id="logList" style="list-style-type: none; padding-left: 0;"></ul>
</div>
`;
document.body.appendChild(gui);
var contributorsList = document.getElementById('contributorsList');
if (!contributorsList) {
console.error('Contributors list container not found.');
return;
}
contributors.forEach(function (contributor) {
var checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.style = "transform: scale(0.75);margin-right:3px;";
checkbox.name = 'contributors';
checkbox.value = contributor.username;
var label = document.createElement('label');
label.style = "font-size:75%";
label.appendChild(checkbox);
label.appendChild(document.createTextNode(`${contributor.username} — ${contributor.edits} edit${contributor.edits === 1 ? '' : 's'}${contributor.isCreator ? ' (Page Creator)' : ''}`));
label.appendChild(document.createElement('br'));
contributorsList.appendChild(label);
});
document.getElementById('submitTranslation').addEventListener('click', function () {
var langCode = document.getElementById('langCode').value.trim();
var articleName = document.getElementById('articleName').value.trim();
var selectedContributors = Array.from(document.querySelectorAll('input[name=contributors]:checked')).map(function (checkbox) {
return checkbox.value;
});
var addTemplate = document.getElementById('addTranslatedPageTemplate').checked;
if (langCode && articleName) {
processTranslation(langCode, articleName, selectedContributors, addTemplate);
document.getElementById('logContainer').style.display = 'block';
} else {
alert('Please fill in both fields.');
}
});
document.getElementById('closeGUI').addEventListener('click', function () {
document.body.removeChild(gui);
});
}
function processTranslation(langCode, articleName, selectedContributors, addTemplate) {
var editSummary = `This article contains content translated from the Wikipedia article at [[${langCode}:${articleName}]]; see its history for attribution. ([[User:Clearfrienda/scripts/AttributeTranslation|AT]])`;
var tasksCompleted = 0;
var totalTasks = 1 + (addTemplate ? 1 : 0) + selectedContributors.length;
var logList = document.getElementById('logList');
function addLogEntry(message, success) {
var listItem = document.createElement('li');
listItem.style.display = 'flex';
listItem.style.alignItems = 'center';
var checkmark = document.createElement('span');
checkmark.style.display = 'inline-block';
checkmark.style.width = '16px';
checkmark.style.height = '16px';
checkmark.style.marginRight = '8px';
checkmark.style.backgroundColor = success ? 'green' : 'red';
checkmark.style.borderRadius = '50%';
listItem.appendChild(checkmark);
listItem.appendChild(document.createTextNode(message));
logList.appendChild(listItem);
}
function checkCompletion() {
tasksCompleted++;
if (tasksCompleted === totalTasks) {
setTimeout(() => {
location.reload();
}, 1000);
}
}
// Dummy edit
$.ajax({
url: mw.util.wikiScript('api'),
type: 'GET',
data: {
format: 'json',
action: 'query',
prop: 'revisions',
rvprop: 'content',
titles: pageTitle
},
success: function (data) {
var pages = data.query.pages;
var pageContent = '';
for (var pageId in pages) {
if (pages.hasOwnProperty(pageId)) {
pageContent = pages[pageId].revisions[0]['*'];
}
}
pageContent += '\n';
$.ajax({
url: mw.util.wikiScript('api'),
type: 'POST',
data: {
format: 'json',
action: 'edit',
title: pageTitle,
text: pageContent,
summary: editSummary,
minor: true,
watchlist: 'nochange',
// watchlistExpiry: '2 weeks',
token: mw.user.tokens.get('csrfToken')
},
success: function () {
console.log('Dummy edit successful');
addLogEntry('Added dummy edit to article', true);
checkCompletion();
},
error: function (jqXHR, textStatus, errorThrown) {
console.error('Error performing dummy edit:', textStatus, errorThrown);
addLogEntry('Failed to add dummy edit to article', false);
checkCompletion();
}
});
},
error: function (jqXHR, textStatus, errorThrown) {
console.error('Error retrieving page content:', textStatus, errorThrown);
addLogEntry('Failed to retrieve page content', false);
checkCompletion();
}
});
// Add {{Translated page}} template
if (addTemplate) {
var talkPageTitle = 'Talk:' + pageTitle;
$.ajax({
url: mw.util.wikiScript('api'),
type: 'POST',
data: {
format: 'json',
action: 'edit',
title: talkPageTitle,
summary: `Adding {{Translated page}} attribution ([[User:Clearfrienda/scripts/AttributeTranslation|AT]])`,
section: 'new',
watchlist: 'nochange',
// watchlistExpiry: '2 weeks',
sectiontitle: '',
text: `{{Translated page|${langCode}|${articleName}|small=no}}`,
token: mw.user.tokens.get('csrfToken')
},
success: function () {
console.log('Article talk page updated');
addLogEntry('Added template to talk page', true);
checkCompletion();
},
error: function (jqXHR, textStatus, errorThrown) {
console.error('Error updating article talk page:', textStatus, errorThrown);
addLogEntry('Failed to add template to talk page', false);
checkCompletion();
}
});
}
// Notify contributors
selectedContributors.forEach(function (contributor) {
notifyContributor(contributor, langCode, articleName);
});
function notifyContributor(contributor, langCode, articleName) {
var subst = "subst:";
var sig = "~~" + "~~";
var notificationMessage = `{{${subst}uw-translation|1=${langCode}:${articleName}|to=${pageTitle}}} ${sig}`;
$.ajax({
url: mw.util.wikiScript('api'),
type: 'POST',
data: {
format: 'json',
action: 'edit',
title: 'User talk:' + contributor,
summary: `Adding non-attributed translation notice ([[User:Clearfrienda/scripts/AttributeTranslation|AT]])`,
section: 'new',
watchlist: 'nochange',
// watchlistExpiry: '2 weeks',
sectiontitle: 'Non-attributed translations',
text: notificationMessage,
token: mw.user.tokens.get('csrfToken')
},
success: function () {
console.log('Notification sent to user talk page');
addLogEntry(`Warned user (${contributor})`, true);
checkCompletion();
},
error: function (jqXHR, textStatus, errorThrown) {
console.error('Error sending notification:', textStatus, errorThrown);
addLogEntry(`Failed to warn user (${contributor})`, false);
if (confirm("Could not notify contributor. Retry?")) {
notifyContributor(contributor, langCode, articleName);
} else {
checkCompletion();
}
}
});
}
}
function getPageContributors(pageTitle, callback) {
$.ajax({
url: mw.util.wikiScript('api'),
type: 'GET',
data: {
format: 'json',
action: 'query',
prop: 'revisions',
rvprop: 'user',
titles: pageTitle,
rvlimit: 'max',
rvdir: 'newer'
},
success: function (data) {
var pages = data.query.pages;
var contributors = [];
for (var pageId in pages) {
if (pages.hasOwnProperty(pageId)) {
var revisions = pages[pageId].revisions;
var creator = true;
revisions.forEach(function (revision) {
if (revision.user && revision.user !== '') {
var contributorIndex = contributors.findIndex(function (item) {
return item.username === revision.user;
});
if (contributorIndex === -1) {
contributors.push({ username: revision.user, edits: 1, isCreator: creator });
} else {
contributors[contributorIndex].edits++;
}
creator = false;
}
});
}
}
callback(contributors);
},
error: function (jqXHR, textStatus, errorThrown) {
console.error('Error retrieving page contributors:', textStatus, errorThrown);
}
});
}
function addButtonToToolbar() {
var allowedNamespaces = [0, 118];
var currentNamespace = mw.config.get('wgNamespaceNumber');
if (allowedNamespaces.includes(currentNamespace)) {
mw.util.addPortletLink(
'p-cactions',
'#',
'Translation attribution',
'ca-translation-attribution',
'AttributeTranslation',
null,
null
);
document.getElementById('ca-translation-attribution').addEventListener('click', function () {
getPageContributors(pageTitle, function (contributors) {
createInputGUI(contributors);
});
});
}
}
addButtonToToolbar();
})();