您需要先安装一个扩展,例如 篡改猴、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);
- })();