Readability

文章可读性提取器 · 提取正文 · 切换主题 · 导出多种格式

输入模式

粘贴文章内容后点击「提取文章」

支持 HTML 或纯文本格式

阅读主题
字体大小
文章统计
字数0
阅读时间0 分钟
段落数0
导出
(function(){ // i18n system const LANG_KEY = 'app_lang'; let currentLang = localStorage.getItem(LANG_KEY) || (navigator.language.startsWith('zh')?'zh':'en'); const i18n = { zh: { pageTitle: 'Readability - 文章可读性提取器 | 在线工具', metaDescription: '免费在线文章可读性提取工具,支持粘贴文章内容或输入URL,提取文章标题/作者/正文,切换阅读主题和字体大小,导出Markdown/纯文本/PDF。', ogTitle: 'Readability - 文章可读性提取器', ogDescription: '免费在线文章可读性提取工具,支持多种主题和字体,导出Markdown/纯文本/PDF。', headerTitle: 'Readability', headerSubtitle: '文章可读性提取器 · 提取正文 · 切换主题 · 导出多种格式', inputModePaste: '粘贴内容', inputModeUrl: '输入 URL', pasteLabel: '粘贴文章内容(HTML 或纯文本)', contentInputPlaceholder: '在此粘贴文章内容 HTML 或纯文本……\n\n提示:可直接粘贴包含

等标签的 HTML 内容', urlLabel: '文章 URL', urlPlaceholder: 'https://example.com/article', fetchUrlBtn: '提取', urlNote: '注意:纯前端无法跨域,建议使用模式1粘贴内容', extractBtn: '提取文章', clearBtn: '清空', placeholderTitle: '粘贴文章内容后点击「提取文章」', placeholderDesc: '支持 HTML 或纯文本格式', themeTitle: '阅读主题', themeDark: '深色', themeLight: '浅色', themeWarm: '暖色', themeEye: '护眼', sizeTitle: '字体大小', sizeSm: '小', sizeMd: '中', sizeLg: '大', sizeXl: '特大', statsTitle: '文章统计', statWords: '字数', statReadTime: '阅读时间', statParagraphs: '段落数', exportTitle: '导出', exportMarkdown: 'Markdown', exportText: '纯文本', exportPdf: 'PDF (打印)', copyContent: '复制文章内容', wordCount: '字', readTimeMinute: '分钟', toastPasteFirst: '请粘贴文章内容', toastExtracted: '文章已提取', toastNoCrossOrigin: '纯前端无法跨域获取 URL 内容,请使用「粘贴内容」模式', toastExtractFirst: '请先提取文章', toastExportedMarkdown: '已导出 Markdown', toastExportedText: '已导出纯文本', toastCopied: '文章内容已复制' }, en: { pageTitle: 'Readability - Article Readability Extractor | Online Tool', metaDescription: 'Free online article readability extraction tool. Paste article content or enter URL, extract title/author/body, switch reading themes and font sizes, export to Markdown/PlainText/PDF.', ogTitle: 'Readability - Article Readability Extractor', ogDescription: 'Free online article readability extraction tool with multiple themes and font sizes, export to Markdown/PlainText/PDF.', headerTitle: 'Readability', headerSubtitle: 'Article Readability Extractor · Extract Content · Switch Themes · Export Formats', inputModePaste: 'Paste Content', inputModeUrl: 'Enter URL', pasteLabel: 'Paste article content (HTML or plain text)', contentInputPlaceholder: 'Paste article content HTML or plain text here...\n\nTip: You can directly paste HTML containing

,

tags', urlLabel: 'Article URL', urlPlaceholder: 'https://example.com/article', fetchUrlBtn: 'Fetch', urlNote: 'Note: Pure frontend cannot cross-origin. Recommend Paste mode.', extractBtn: 'Extract', clearBtn: 'Clear', placeholderTitle: 'Paste article content then click "Extract"', placeholderDesc: 'Supports HTML or plain text', themeTitle: 'Reading Theme', themeDark: 'Dark', themeLight: 'Light', themeWarm: 'Warm', themeEye: 'Eye-care', sizeTitle: 'Font Size', sizeSm: 'Small', sizeMd: 'Medium', sizeLg: 'Large', sizeXl: 'Extra Large', statsTitle: 'Article Stats', statWords: 'Words', statReadTime: 'Read Time', statParagraphs: 'Paragraphs', exportTitle: 'Export', exportMarkdown: 'Markdown', exportText: 'Plain Text', exportPdf: 'PDF (Print)', copyContent: 'Copy Content', wordCount: 'characters', readTimeMinute: 'min', toastPasteFirst: 'Please paste article content', toastExtracted: 'Article extracted', toastNoCrossOrigin: 'Cannot fetch URL cross-origin. Use Paste mode.', toastExtractFirst: 'Please extract an article first', toastExportedMarkdown: 'Exported as Markdown', toastExportedText: 'Exported as plain text', toastCopied: 'Content copied' } }; function t(key) { const pack = i18n[currentLang]; return (pack && pack[key]) || (i18n['zh'][key]) || key; } // Global error handler window.onerror = function(msg, url, line, col, err) { console.error('[Global Error]', msg, 'at', url, line, col); }; window.addEventListener('unhandledrejection', function(e) { console.error('[Unhandled Promise Rejection]', e.reason); }); // Apply i18n to HTML elements function applyI18n() { // title const titleEl = document.querySelector('title[data-i18n]'); if(titleEl) document.title = t(titleEl.getAttribute('data-i18n')); // meta with data-i18n-content document.querySelectorAll('meta[data-i18n-content]').forEach(function(el){ const key = el.getAttribute('data-i18n-content'); if(key) el.setAttribute('content', t(key)); }); // elements with data-i18n document.querySelectorAll('[data-i18n]').forEach(function(el){ if(el.tagName === 'TITLE' || el.tagName === 'META') return; const key = el.getAttribute('data-i18n'); if(key) el.textContent = t(key); }); // placeholders document.querySelectorAll('[data-i18n-placeholder]').forEach(function(el){ const key = el.getAttribute('data-i18n-placeholder'); if(key) el.setAttribute('placeholder', t(key)); }); } // Override showToast window.showToast = function(msg) { const el = document.getElementById('toast'); if(!el) return; el.textContent = msg; el.classList.add('show'); clearTimeout(el._timer); el._timer = setTimeout(function(){ el.classList.remove('show'); }, 2400); }; // Override extractArticle window.extractArticle = function() { const raw = document.getElementById('contentInput').value.trim(); if (!raw) { showToast(t('toastPasteFirst')); return; } let title = ''; let author = ''; let contentHtml = ''; let contentText = raw; const parser = new DOMParser(); const doc = parser.parseFromString(raw, 'text/html'); const hasHtml = doc.body && doc.body.children.length > 0 && !raw.startsWith(' '); if (hasHtml && doc.body.innerHTML !== raw) { const h1 = doc.querySelector('h1'); if (h1) title = h1.textContent.trim(); else { const h2 = doc.querySelector('h2'); if (h2) title = h2.textContent.trim(); } const metaAuthor = doc.querySelector('meta[name="author"]'); if (metaAuthor) author = metaAuthor.content; if (!author) { const byline = doc.querySelector('.byline, .author, [rel="author"]'); if (byline) author = byline.textContent.trim(); } if (!author) { const timeEl = doc.querySelector('time'); if (timeEl) author = timeEl.textContent.trim(); } contentHtml = doc.body.innerHTML; contentText = doc.body.textContent.trim(); } else { const lines = raw.split('\n').filter(function(l){ return l.trim(); }); if (lines.length > 0) { title = lines[0].trim(); if (title.length > 80) title = ''; } contentText = raw; contentHtml = raw.split('\n').filter(function(l){ return l.trim(); }).map(function(l){ return '

' + escHtml(l) + '

'; }).join('\n'); } if (!title) title = '无标题文章'; contentText = contentText.replace(/\s+/g, ' ').trim(); window.currentArticle = { title: title, author: author, content: contentText, html: contentHtml }; renderArticle(); updateStats(); }; // Override renderArticle window.renderArticle = function() { const ra = document.getElementById('readingArea'); if(!ra) return; const article = window.currentArticle; const authorLine = article.author ? ' · ' + escHtml(article.author) : ''; ra.innerHTML = '
' + escHtml(article.title) + '
' + article.content.length + ' ' + t('wordCount') + authorLine + '
' + article.html + '
'; showToast(t('toastExtracted')); }; // Override updateStats window.updateStats = function() { const article = window.currentArticle; const text = article.content; const wordCount = text.length; const readTime = Math.max(1, Math.ceil(wordCount / 300)); const paras = text.split('\n').filter(function(l){ return l.trim(); }).length; var el; el = document.getElementById('statWords'); if(el) el.textContent = wordCount.toLocaleString(); el = document.getElementById('statReadTime'); if(el) el.textContent = readTime + ' ' + t('readTimeMinute'); el = document.getElementById('statParagraphs'); if(el) el.textContent = paras; }; // Helper function escHtml(str) { var d = document.createElement('div'); d.textContent = str; return d.innerHTML; } // Rebind events by cloning to remove old listeners function rebindButton(id, handler) { var btn = document.getElementById(id); if (!btn) return; var newBtn = btn.cloneNode(true); btn.parentNode.replaceChild(newBtn, btn); newBtn.addEventListener('click', handler); } // Bind handlers var handlers = { extractBtn: function() { extractArticle(); }, clearBtn: function() { document.getElementById('contentInput').value = ''; document.getElementById('urlInput').value = ''; window.currentArticle = { title: '', author: '', content: '', html: '' }; var ra = document.getElementById('readingArea'); if(ra) ra.innerHTML = '

' + t('placeholderTitle') + '

' + t('placeholderDesc') + '

'; var el; el = document.getElementById('statWords'); if(el) el.textContent = '0'; el = document.getElementById('statReadTime'); if(el) el.textContent = '0 ' + t('readTimeMinute'); el = document.getElementById('statParagraphs'); if(el) el.textContent = '0'; }, fetchUrlBtn: function() { showToast(t('toastNoCrossOrigin')); }, exportMarkdown: function() { var art = window.currentArticle; if (!art.content) { showToast(t('toastExtractFirst')); return; } var md = '# ' + art.title + '\n\n'; if (art.author) md += '> 作者:' + art.author + '\n\n'; md += art.content + '\n'; var blob = new Blob([md], {type:'text/markdown'}); var a = document.createElement('a'); a.download = (art.title || 'article') + '.md'; a.href = URL.createObjectURL(blob); a.click(); URL.revokeObjectURL(a.href); showToast(t('toastExportedMarkdown')); }, exportText: function() { var art = window.currentArticle; if (!art.content) { showToast(t('toastExtractFirst')); return; } var txt = art.title + '\n' + '='.repeat(art.title.length) + '\n\n'; if (art.author) txt += '作者:' + art.author + '\n\n'; txt += art.content; var blob = new Blob([txt], {type:'text/plain'}); var a = document.createElement('a'); a.download = (art.title || 'article') + '.txt'; a.href = URL.createObjectURL(blob); a.click(); URL.revokeObjectURL(a.href); showToast(t('toastExportedText')); }, exportPdf: function() { var art = window.currentArticle; if (!art.content) { showToast(t('toastExtractFirst')); return; } window.print(); }, copyContentBtn: function() { var art = window.currentArticle; if (!art.content) { showToast(t('toastExtractFirst')); return; } var text = '# ' + art.title + '\n\n' + art.content; if (navigator.clipboard) { navigator.clipboard.writeText(text).then(function(){ showToast(t('toastCopied')); }).catch(function(){ }); } else { var ta = document.createElement('textarea'); ta.value = text; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); ta.remove(); showToast(t('toastCopied')); } } }; // Apply i18n and rebind after DOM ready function init() { applyI18n(); Object.keys(handlers).forEach(function(id){ rebindButton(id, handlers[id]); }); } // Ensure DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })(); window.addEventListener('unhandledrejection', function(e) { console.warn('Unhandled Promise Rejection:', e.reason); e.preventDefault(); });