您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
GitHub sayfalarındaki Latin olmayan karakterli metinleri resmi olmayan Google Translate API'sini kullanarak çevirir.
// ==UserScript== // @name Multi-Language Translator (Unofficial) // @namespace http://tampermonkey.net/ // @version 1.2 // @description GitHub sayfalarındaki Latin olmayan karakterli metinleri resmi olmayan Google Translate API'sini kullanarak çevirir. // @author ekstra26 // @license MIT // @match https://github.com/* // @grant GM_xmlhttpRequest // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @connect translate.googleapis.com // ==/UserScript== (function() { 'use strict'; // --- Kullanıcının Ayarları --- let TARGET_SOURCE_LANGS_CODES = GM_getValue('targetSourceLangCodes', 'zh,ja,ko'); const TARGET_LANG = 'en'; // Hedef dil (İngilizce) // Unicode karakter aralıkları (basit dil/script tespiti için) const LANG_RANGES = { 'zh': { name: 'Çince (Basit/Geleneksel), Japonca (Kanji), Korece (Hanja)', regex: /[\u4E00-\u9FFF\u3400-\u4DBF]/ }, 'ja': { name: 'Japonca (Hiragana/Katakana)', regex: /[\u3040-\u30FF]/ }, 'ko': { name: 'Korece (Hangul)', regex: /[\uAC00-\uD7AF]/ }, 'ru': { name: 'Rusça (Kiril)', regex: /[\u0400-\u04FF]/ }, 'ar': { name: 'Arapça', regex: /[\u0600-\u06FF\u0750-\u077F]/ }, 'el': { name: 'Yunanca', regex: /[\u0370-\u03FF\u1F00-\u1FFF]/ }, 'th': { name: 'Tayca', regex: /[\u0E00-\u0E7F]/ }, }; GM_registerMenuCommand("Taranacak Kaynak Dilleri Ayarla", function() { const availableLangs = Object.keys(LANG_RANGES).map(code => `${code} (${LANG_RANGES[code].name})`).join('\n'); let newLangs = prompt( `Lütfen taranacak dil kodlarını virgülle ayırarak girin (örn: zh,ja,ko).\n` + `Mevcut ve önerilen diller:\n` + `${availableLangs}\n\n` + `Not: Bu ayar, metinlerin hangi karakterleri içerdiğini tespit etmek için kullanılır. ` + `API'nin kendisi çoğu zaman doğru dilde çeviri yapar.`, TARGET_SOURCE_LANGS_CODES ); if (newLangs !== null) { TARGET_SOURCE_LANGS_CODES = newLangs.trim().toLowerCase(); GM_setValue('targetSourceLangCodes', TARGET_SOURCE_LANGS_CODES); alert(`Taranacak diller başarıyla kaydedildi: ${TARGET_SOURCE_LANGS_CODES}. Sayfayı yenileyerek çeviriyi başlatın.`); } else { alert('Dil ayarı iptal edildi.'); } }); GM_registerMenuCommand("Şimdi Çeviriyi Başlat", function() { if (!GM_getValue('targetSourceLangCodes')) { showNotification("Lütfen önce 'Taranacak Kaynak Dilleri Ayarla' seçeneğini kullanarak dilleri ayarlayın.", 'error'); return; } console.log("[DEBUG] Manuel çeviri başlatılıyor..."); translatePage(); }); // --- Yardımcı Fonksiyonlar --- function containsAnySelectedNonLatinScript(text) { const selectedCodes = TARGET_SOURCE_LANGS_CODES.split(',').map(c => c.trim()); for (const code of selectedCodes) { const langInfo = LANG_RANGES[code]; if (langInfo && langInfo.regex.test(text)) { return true; } } return false; } function translateText(text, targetLang) { return new Promise((resolve, reject) => { const encodedText = encodeURIComponent(text); const apiUrl = `https://translate.googleapis.com/translate_a/single?client=gtx&dt=t&sl=auto&tl=${targetLang}&q=${encodedText}`; console.log(`[DEBUG] API çağrısı yapılıyor (ilk 100 karakter): ${apiUrl.substring(0, 100)}...`); // URL'yi kısalt console.log(`[DEBUG] Orijinal Metin gönderiliyor: "${text}"`); GM_xmlhttpRequest({ method: "GET", url: apiUrl, onload: function(response) { console.log(`[DEBUG] API Yanıt Durumu: ${response.status}`); console.log(`[DEBUG] API Yanıt Metni (tamamını):`, response.responseText); // Tam yanıtı logla try { const data = JSON.parse(response.responseText); if (data && data[0] && data[0][0] && data[0][0][0]) { const translated = data[0][0][0]; console.log(`[DEBUG] Çevrilen Metin (parsed): "${translated}"`); resolve(translated); } else { console.warn("[DEBUG] Çeviri sonucu bulunamadı veya beklenmeyen yanıt yapısı:", data); reject("Çeviri sonucu bulunamadı veya API yanıt yapısı değişmiş olabilir."); } } catch (e) { console.error("[DEBUG] JSON parse hatası veya API yanıtı işlenirken hata oluştu:", e); reject("API yanıtı işlenirken hata oluştu. Yanıt: " + response.responseText); } }, onerror: function(response) { console.error("[DEBUG] API çağrısı başarısız oldu (onerror):", response); reject(`Ağ hatası veya API çağrısı başarısız. Durum kodu: ${response.status}, Yanıt: ${response.responseText}`); }, ontimeout: function() { console.error("[DEBUG] API çağrısı zaman aşımına uğradı."); reject("API çağrısı zaman aşımına uğradı."); } }); }); } function showNotification(message, type = 'info') { let notification = document.getElementById('gh-translate-notification'); if (!notification) { notification = document.createElement('div'); notification.id = 'gh-translate-notification'; notification.style.position = 'fixed'; notification.style.bottom = '20px'; notification.style.right = '20px'; notification.style.background = '#333'; notification.style.color = 'white'; notification.style.padding = '10px 15px'; notification.style.borderRadius = '5px'; notification.style.zIndex = '99999'; notification.style.display = 'none'; notification.style.opacity = '0.9'; notification.style.fontSize = '14px'; notification.style.boxShadow = '0 2px 10px rgba(0,0,0,0.5)'; document.body.appendChild(notification); } notification.textContent = message; notification.style.background = type === 'error' ? '#d9534f' : (type === 'success' ? '#5cb85c' : '#333'); notification.style.display = 'block'; setTimeout(() => { notification.style.display = 'none'; }, 8000); } // --- Ana Çeviri Mantığı --- async function translatePage() { if (!TARGET_SOURCE_LANGS_CODES) { showNotification("Lütfen önce 'Taranacak Kaynak Dilleri Ayarla' seçeneğini kullanarak dilleri ayarlayın.", 'error'); return; } showNotification(`Sayfa taranıyor ve ${TARGET_SOURCE_LANGS_CODES} dillerinden metinler çevriliyor...`); console.log(`[DEBUG] Taranacak diller: ${TARGET_SOURCE_LANGS_CODES}`); const textNodesToTranslate = []; const uniqueTexts = new Set(); // GitHub README, Wiki, issue yorumları gibi ana içerik alanlarını hedefleyin. // Daha genel p etiketlerini de ekledim. const containers = document.querySelectorAll( '.markdown-body,p,h1,h2,h3, .wiki-body, .js-issue-row .col-9, .js-comment-body, .f5.color-fg-muted, .comment-body, .Box-body p, .BorderGrid-cell p' ); console.log(`[DEBUG] ${containers.length} adet olası çeviri konteyneri bulundu.`); containers.forEach(container => { const walk = document.createTreeWalker(container, NodeFilter.SHOW_TEXT, null, false); let node; while ((node = walk.nextNode())) { const text = node.nodeValue.trim(); if (text !== '' && node.parentNode && node.parentNode.nodeName !== 'SCRIPT' && node.parentNode.nodeName !== 'STYLE' && node.parentNode.nodeName !== 'CODE' && node.parentNode.nodeName !== 'PRE' && node.parentNode.nodeName !== 'KBD' && !node.parentNode.classList.contains('highlight') && text.length > 5 && // Çok kısa metinleri atla (örn: "a", "1", "...") containsAnySelectedNonLatinScript(text) && // Sadece seçilen dillerin karakterlerini içerenleri tara !uniqueTexts.has(text) // Aynı metni birden fazla kez işlememek için ) { textNodesToTranslate.push({ node: node, originalText: text }); uniqueTexts.add(text); // console.log(`[DEBUG] Çevrilecek metin düğümü bulundu: "${text.substring(0, Math.min(text.length, 50))}..."`); } } }); if (textNodesToTranslate.length === 0) { showNotification("Çevrilecek metin bulunamadı (seçilen dillerde).", 'info'); console.log("[DEBUG] Çevrilecek metin düğümü bulunamadı (seçilen dillerde)."); return; } showNotification(`${textNodesToTranslate.length} adet taranan metin bulundu. Çeviriliyor... (Bu işlem biraz zaman alabilir)`); console.log(`[DEBUG] Toplam ${textNodesToTranslate.length} adet metin düğümü çeviri için kuyruğa alındı.`); let translatedCount = 0; let failedCount = 0; // Her metin düğümünü API'nin limitlerine takılmamak için sırayla çevirelim for (const item of textNodesToTranslate) { const node = item.node; const originalText = item.originalText; try { const translatedText = await translateText(originalText, TARGET_LANG); console.log(`[DEBUG] Orijinal: "${originalText}" -> Çevrilen: "${translatedText}"`); if (translatedText && translatedText.trim() !== originalText.trim()) { node.nodeValue = translatedText; translatedCount++; console.log(`[DEBUG] Metin başarıyla güncellendi. Yeni metin: "${node.nodeValue}"`); } else { console.log(`[DEBUG] Metin güncellenmedi. Orijinal metinle aynıydı veya boş döndü. (Orijinal: "${originalText}", Gelen: "${translatedText}")`); } } catch (error) { console.warn(`[DEBUG] Metin çevrilemedi: "${originalText.substring(0, Math.min(originalText.length, 50))}..." Hata: ${error}`); failedCount++; } // Her API çağrısı arasında kısa bir gecikme await new Promise(resolve => setTimeout(resolve, 100)); } if (translatedCount > 0) { showNotification(`${translatedCount} metin başarıyla çevrildi. ${failedCount} metin çevrilemedi.`, 'success'); } else { showNotification("Taranan metinler ya zaten hedef dildeydi ya da çevrilecek metin bulunamadı.", 'info'); } console.log("[DEBUG] Çeviri işlemi tamamlandı."); } // Sayfa yüklendiğinde ve biraz sonra tekrar çeviriyi dene window.addEventListener('load', translatePage); setTimeout(translatePage, 3000); })();