feat(miniflux): transalte entries
This commit is contained in:
parent
6d2360fdd8
commit
86b743457b
134
miniflux_scripts/translate_entries.js
Normal file
134
miniflux_scripts/translate_entries.js
Normal file
@ -0,0 +1,134 @@
|
||||
// ==UserScript==
|
||||
// @name Miniflux: Translate Entry to French
|
||||
// @namespace https://zoemp.be
|
||||
// @version 0.7
|
||||
// @description Translate Miniflux entries to French using SimplyTranslate (Google engine)
|
||||
// @include /^https?:\/\/[^\/]+\/.*\/entry\/.*/
|
||||
// @grant GM_xmlhttpRequest
|
||||
// @run-at document-end
|
||||
// ==/UserScript==
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
console.log('[Miniflux Translate] Loaded on URL:', location.href);
|
||||
|
||||
const isProbablyEnglish = (text) => {
|
||||
const ratio = (text.match(/[a-z]/gi) || []).length / text.length;
|
||||
const isLong = text.length > 100;
|
||||
const hasWords = /[a-z]{5,}/.test(text);
|
||||
const result = ratio > 0.4 && isLong && hasWords;
|
||||
console.log(`[Miniflux Translate] Heuristic result: ${result} (ratio: ${ratio.toFixed(2)}, length: ${text.length})`);
|
||||
return result;
|
||||
};
|
||||
|
||||
const decodeEntities = (str) => {
|
||||
return str
|
||||
.replace(/'/g, `'`)
|
||||
.replace(/"/g, `"`)
|
||||
.replace(/"/g, `"`)
|
||||
.replace(/&/g, `&`)
|
||||
.replace(/</g, `<`)
|
||||
.replace(/>/g, `>`)
|
||||
.replace(/'/g, `'`)
|
||||
.replace(/ /g, ' ')
|
||||
.replace(/ /g, ' ')
|
||||
.replace(/&#(\d+);/g, (_, n) => String.fromCharCode(n));
|
||||
};
|
||||
|
||||
const translate = (text, cb) => {
|
||||
const form = `from=en&to=fr&text=${encodeURIComponent(text)}`;
|
||||
console.log('[Miniflux Translate] Sending translation request...');
|
||||
GM_xmlhttpRequest({
|
||||
method: 'POST',
|
||||
url: 'https://simplytranslate.org/?engine=google',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Accept': 'text/html',
|
||||
'Origin': 'null'
|
||||
},
|
||||
data: form,
|
||||
onload: (res) => {
|
||||
const match = res.responseText.match(/<textarea[^>]*id="output"[^>]*>([^<]*)<\/textarea>/);
|
||||
if (match && match[1]) {
|
||||
const raw = match[1];
|
||||
const cleaned = decodeEntities(raw);
|
||||
console.log('[Miniflux Translate] Translation successful');
|
||||
cb(cleaned);
|
||||
} else {
|
||||
console.warn('[Miniflux Translate] Failed to extract translation');
|
||||
cb('[Error: Translation not found]');
|
||||
}
|
||||
},
|
||||
onerror: () => {
|
||||
console.error('[Miniflux Translate] Network error during translation request');
|
||||
cb('[Error: Network issue]');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const injectButton = () => {
|
||||
const content = document.querySelector('.entry-content');
|
||||
if (!content) {
|
||||
console.warn('[Miniflux Translate] .entry-content not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const text = content.innerText.trim();
|
||||
if (!isProbablyEnglish(text)) {
|
||||
console.log('[Miniflux Translate] Entry appears to be in French, skipping');
|
||||
return;
|
||||
}
|
||||
|
||||
if (content.querySelector('.translate-btn')) {
|
||||
console.log('[Miniflux Translate] Button already exists');
|
||||
return;
|
||||
}
|
||||
|
||||
const btn = document.createElement('button');
|
||||
btn.textContent = '🈯 Traduire en français';
|
||||
btn.className = 'translate-btn';
|
||||
btn.style.cssText = `
|
||||
margin-top: 12px;
|
||||
padding: 6px 12px;
|
||||
font-size: 0.9em;
|
||||
cursor: pointer;
|
||||
background-color: #222;
|
||||
color: #fff;
|
||||
border: 1px solid #444;
|
||||
border-radius: 4px;
|
||||
`;
|
||||
|
||||
btn.onclick = () => {
|
||||
btn.disabled = true;
|
||||
btn.textContent = '⏳ Traduction en cours...';
|
||||
translate(text, (translated) => {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = translated;
|
||||
div.style.cssText = `
|
||||
margin-top: 12px;
|
||||
padding: 10px;
|
||||
background: rgb(51 85 67);
|
||||
border-left: 3px solid rgb(71 180 103);
|
||||
white-space: pre-wrap;
|
||||
font-family: "Geist Mono";
|
||||
`;
|
||||
content.appendChild(div);
|
||||
btn.remove();
|
||||
});
|
||||
};
|
||||
|
||||
content.appendChild(btn);
|
||||
console.log('[Miniflux Translate] Button injected');
|
||||
};
|
||||
|
||||
const waitUntilReady = () => {
|
||||
if (document.readyState !== 'complete') {
|
||||
console.log('[Miniflux Translate] Waiting for document...');
|
||||
return setTimeout(waitUntilReady, 100);
|
||||
}
|
||||
injectButton();
|
||||
};
|
||||
|
||||
waitUntilReady();
|
||||
})();
|
Loading…
x
Reference in New Issue
Block a user