您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add a button on the BGA translation page that allows all the text to be translated on the page to be placed in a text block for easy copying.
// ==UserScript== // @name BGA Translation TextBox Export // @version 1.1.0 // @namespace ani20168 // @description Add a button on the BGA translation page that allows all the text to be translated on the page to be placed in a text block for easy copying. // @author ani20168 // @license MIT // @match https://boardgamearena.com/translation* // @icon https://t0.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://boardgamearena.com&size=64 // @grant none // ==/UserScript== (function () { 'use strict'; /*** 常數 ***/ const SEPARATOR = '----------------------------------'; // 分隔線 /*** 主程式 ***/ function setup() { // 避免重複建立 if (document.getElementById('textBoxExportButton')) { return; } /* ---------- 1. Export 按鈕 ---------- */ const exportButton = document.createElement('input'); exportButton.type = 'button'; exportButton.value = 'TextBox Export'; exportButton.id = 'textBoxExportButton'; exportButton.className = 'bgabutton bgabutton_blue'; exportButton.style.marginLeft = '4px'; const exportArea = document.createElement('textarea'); exportArea.style.cssText = 'width:100%;height:200px;display:none;'; exportArea.readOnly = true; exportButton.onclick = () => { if (exportArea.style.display === 'block') { exportArea.style.display = 'none'; } else { const raws = Array.from(document.getElementsByClassName('readonly_textarea')) .map(t => t.value.trim()); exportArea.value = raws.join('\n' + SEPARATOR + '\n'); exportArea.style.display = 'block'; } }; /* ---------- 2. Import 按鈕 ---------- */ const importButton = document.createElement('input'); importButton.type = 'button'; importButton.value = 'TextBox Import'; importButton.id = 'textBoxImportButton'; importButton.className = 'bgabutton bgabutton_blue'; importButton.style.marginLeft = '4px'; const importArea = document.createElement('textarea'); importArea.style.cssText = 'width:100%;height:200px;display:none;'; importButton.onclick = () => { if (importArea.style.display === 'block') { // 第二次點擊 → 解析並分配 distributeTranslations(importArea.value); importArea.style.display = 'none'; } else { importArea.style.display = 'block'; } }; /* ---------- 3. 把按鈕插入原本 UI ---------- */ const resetFilterButton = document.querySelector('#findreset_form > span'); resetFilterButton.appendChild(exportButton); resetFilterButton.appendChild(importButton); const btnContainer = document.getElementById('filters_buttons'); btnContainer.appendChild(exportArea); btnContainer.appendChild(importArea); } /*** * 把整段翻譯文字依 SEPARATOR 切開並分配到每個 translation_block * @param {string} rawText - 使用者貼上的整份翻譯結果 */ function distributeTranslations(rawText) { const pieces = rawText .split(new RegExp(`\\s*${SEPARATOR}\\s*`, 'g')) .map(t => t.trim()) .filter(Boolean); const blocks = Array.from(document.getElementsByClassName('translation_block')); let idx = 0; blocks.forEach(block => { const readonly = block.querySelector('.readonly_textarea'); const translated = block.querySelector('textarea[id^="translated_"]'); if (!readonly || !translated) { return; } // 保險檢查 // 如果還有對應翻譯就建立 UI const piece = pieces[idx++]; if (piece !== undefined) { addLoadUI(block, translated, piece); } }); } /** * 在指定翻譯區塊右側新增「載入文字框」+「確認載入」按鈕 * @param {HTMLElement} block - .translation_block * @param {HTMLTextAreaElement} translatedArea - 原本用來輸入翻譯的 textarea * @param {string} piece - 對應的翻譯文字 */ function addLoadUI(block, translatedArea, piece) { // 若已經加過就僅更新內容 let loadDiv = block.querySelector('.loaded_translation_zone'); if (!loadDiv) { loadDiv = document.createElement('div'); loadDiv.className = 'loaded_translation_zone translation_meta'; // textarea const loadTA = document.createElement('textarea'); loadTA.className = 'loaded_textarea'; loadTA.style.cssText = 'width:120%;height:140px;margin-top:4px;box-sizing:border-box;'; loadDiv.appendChild(loadTA); // button const btnWrap = document.createElement('div'); btnWrap.style.display = 'inline-block'; const confirmBtn = document.createElement('a'); confirmBtn.href = 'javascript:false;'; confirmBtn.tabIndex = -1; confirmBtn.className = 'bgabutton bgabutton_blue'; confirmBtn.textContent = '確認載入'; btnWrap.appendChild(confirmBtn); loadDiv.appendChild(btnWrap); // 行為:點下"確認載入" confirmBtn.onclick = () => { // 1) 把翻譯文字塞進官方 textarea translatedArea.value = loadTA.value.trim(); // 2) 觸發 input 事件,讓網站知道值變了 translatedArea.dispatchEvent(new Event('input', { bubbles: true })); // 3) 模擬「點到 → 離開」以觸發自動上傳 translatedArea.focus(); // 用 setTimeout 保證事件順序:先 focus,再 blur setTimeout(() => translatedArea.blur(), 0); // 4) UI 標示 confirmBtn.textContent = '已載入'; confirmBtn.classList.add('disabled'); }; // 插入到右側 col-md-6 的最下方 const rightCol = block.querySelector('.col-md-6'); rightCol.appendChild(loadDiv); } // 更新文字 loadDiv.querySelector('.loaded_textarea').value = piece; } /* ---------- 啟動監聽 ---------- */ new MutationObserver(setup).observe(document, { childList: true, subtree: true }); setup(); })();