Translator | 2026

Chat Translation Assistant

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey .

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name             Translator | 2026
// @namespace        https://github.com/GameSketchers
// @version          0.1
// @description      Chat Translation Assistant
// @description:tr   Sohbet Çeviri Yardımcısı
// @description:pt   Assistente de Tradução de Chat
// @description:ru   Помощник перевода чата
// @description:es   Asistente de Traducción de Chat
// @description:fr   Assistant de Traduction de Chat
// @description:de   Chat-Übersetzungsassistent
// @description:ja   チャット翻訳アシスタント
// @description:ko   채팅 번역 도우미
// @description:zh   聊天翻译助手
// @description:ar   مساعد ترجمة الدردشة
// @author           Qwyua
// @match            *://gartic.io/*
// @run-at           document-end
// @grant            unsafeWindow
// @grant            GM_xmlhttpRequest
// @connect          translate.googleapis.com
// @license          MIT
// ==/UserScript==
// obsolescencethysen istegi uzerine
/gartic\.io$/.test(location.host)&&(()=>{
    const onElementAdded4=(s,c,m=1)=>{let o,d=0,p=e=>(d||c(e),m||(d=1,o?.disconnect())),t=()=>{o?.disconnect(),d=0,o=new MutationObserver(r=>{for(let i=0;i<r.length;i++)for(let n of r[i].addedNodes)n.nodeType===1&&(n.matches?.(s)?p(n):(n.querySelector?.(s)&&p(n.querySelector(s))))}),o.observe(document.documentElement,{childList:1,subtree:1});document.querySelector(s)&&p(document.querySelector(s))};t();return{stop:()=>(o?.disconnect(),d=1),start:()=>(o?.disconnect(),t()),get active(){return!d}}};
    ;(o=>o[Object.keys(o).find(k=>location.href.includes(k))]?.())({
        gartic:()=>{
            new class{
                constructor(){
                    const w=unsafeWindow
                    this._translator=w._translator||(w._translator={})
                    Object.defineProperty(this._translator,"self",{value:this,enumerable:0,configurable:1,writable:1})
                    onElementAdded4("div#screenRoom",e=>{let g,c,k,f=n=>n&&!g&&(n.tag==1&&(c=n.stateNode?.props?.children?.[0]?._owner?.stateNode)&&(g=this._translator._game=c._game)&&(g._chat=c._chatElem,this.joinedRoom(g)),!g&&f(n.child));for(k in e)/^__r/.test(k)&&f(e[k])},1)
                    this.langs={
                        "none": "Off",
                        "en": "English",
                        "tr": "Türkçe",
                        "pt": "Português",
                        "ru": "Русский",
                        "es": "Español",
                        "fr": "Français",
                        "de": "Deutsch",
                        "it": "Italiano",
                        "zh": "中文",
                        "hi": "हिन्दी",
                        "ar": "العربية",
                        "bn": "বাংলা",
                        "ja": "日本語",
                        "ko": "한국어"
                    }
                    this.readLanguage=localStorage.getItem("readLanguage")||"auto"
                    this.seenLanguage=localStorage.getItem("seenLanguage")||(navigator.language||navigator.userLanguage)
                    this.sendLanguage=localStorage.getItem("sendLanguage")||"none"
                    this.translateActive=!1
                    this.smartMode=1
                    this.spamDejected=0
                    this.messageQueue = [];
                }
                isOnlySymbols = (msg) => !/[\p{L}\p{N}]/u.test(msg);
                async translate(MMq,tLang,rLang) {
                    const isArray = Array.isArray(MMq);
                    const query = isArray?MMq.map(m=>encodeURIComponent(m)).join('&q='):encodeURIComponent(MMq);
                    const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=${rLang||"auto"}&tl=${tLang}&dt=t&q=${query}`;
                    return new Promise(resolve=>{
                        GM_xmlhttpRequest({
                            method: 'GET',
                            url: url,
                            onload:(x)=>{
                                const data=JSON.parse(x.responseText);
                                isArray&&(resolve(data[0].map(item=>item[0])),0);
                                !isArray&&(()=>{
                                    const result=data[2]==this.readLanguage?MMq:data[0][0][0];
                                    const fail=!result&&this.translateActive;
                                    this.spamDejected=fail?this.spamDejected+1:0;
                                    (fail&&this.spamDejected>=5)&&(document.querySelector("#Translation button").click(),this.spamDejected=0);
                                    resolve(result||MMq);
                                })();
                            },
                            onerror:()=>resolve(MMq)
                        });
                    });
                }
                async joinedRoom(){
                    onElementAdded4("#chat h5",h5=>{document.getElementById('TranslationTrigger')||(this.h5=h5,this.injectHTML())},0);
                    this.timeoutId = null;
                    this.addCHATMessage = this._translator._game._socket._callbacks["$11"][0].bind(this._translator._game._socket._callbacks["$11"][0]);
                    this._translator._game._socket._callbacks["$11"][0]=async(playerID,message)=>{
                        this.isOnlySymbols(message)||message.length<5||this._translator._game._id==playerID||!this.translateActive||this.sendLanguage=="auto"||this.seenLanguage=='none'?this.addCHATMessage(playerID,message):(
                            this.messageQueue.push({playerID,message}),
                            this.timeoutId && clearTimeout(this.timeoutId),
                            this.timeoutId=setTimeout(async()=>{
                                const translated = await this.translate(this.messageQueue.map(m=>m.message),this.seenLanguage);
                                this.messageQueue.forEach((q,w)=>this.addCHATMessage(q.playerID,translated[w]||q.message));
                                this.messageQueue=[];
                                this.timeoutId=null;
                            },600)
                        );
                    };
                    this.sendMessageCHAT=this._translator._game.message.bind(this._translator._game);
                    this._translator._game.message=async(t)=>{
                        this.isOnlySymbols(t)||!this.translateActive||this.sendLanguage=="auto"||this.sendLanguage=='none'?this.sendMessageCHAT(t):(async()=>{
                            const Mq=await this.translate(t,this.sendLanguage);
                            this.sendMessageCHAT(this.smartMode?this.simplifyText(Mq):Mq);
                        })()
                    }
                }
                syncPos=()=>{const{bottom:b,right:r}=this.h5.getBoundingClientRect();Object.assign(this.control.style,{top:`${b+8}px`,left:`${r-190}px`})};
                setLanguage=(q,w)=>{this[q+"Language"]=w;localStorage.setItem(q+"Language",w)};
                startStopTranslation(q){this.translateActive=q.classList.toggle('active');q.textContent=this.translateActive?'ON':'OFF'}
                switchSmartMode(q){this.smartMode=q.classList.toggle('active');q.textContent=this.smartMode?'ON':'OFF'}
                simplifyText(m){
                    let t=m.toLowerCase().replace(/[!?.,;:()\[\]{}"']/g,'');
                    const s={you:'u',are:'re',your:'ur',"you're":'ur',because:'cuz',please:'pls',thanks:'thx',language:'lang',okay:'ok'};
                    Object.entries(s).forEach(([k,v])=>t=t.replace(new RegExp(`\\b${k}\\b`,'g'),v));
                    return t;
                }
                injectHTML() {
                    this.trigger = Object.assign(document.createElement('span'),{
                        id: 'TranslationTrigger',
                        textContent: '\uE941',
                        onclick:e=>{e.stopPropagation();this.control.style.display=this.control.style.display==='block'?'none':(this.syncPos(),'block')},
                        style: 'font-family:ico;color:#3080cb;cursor:pointer;margin-left:8px;font-size:15px;vertical-align:middle;display:inline-block;transform:rotate(20deg)'
                    });
                    this.h5.appendChild(this.trigger);
                    this.control = Object.assign(document.createElement('div'),{
                        id:'Translation',
                        onchange:(q)=>{this._translator?.self.setLanguage(q.target.id,q.target.value)},
                        innerHTML: `<style>#Translation{position:fixed;display:none;background:#11223e;border:2px solid #234476;border-radius:6px;padding:12px;width:190px;z-index:9e6;box-shadow:0 6px 20px #000;box-sizing:border-box;font-family:sans-serif}#Translation div{display:flex;align-items:center;margin-bottom:5px;gap:8px}#Translation label{font-size:12px;font-weight:700;color:#8bb4e7;width:45px;flex-shrink:0}#Translation div div{display:flex;flex:1;gap:4px}#Translation select{background:#1b335a;border:1px solid #2d558d;color:#fff;border-radius:4px;font-size:12px;padding:4px;width:100%;cursor:pointer;outline:none}#Translation button{width:100%;padding:7px;border:none;border-radius:4px;font-weight:900;font-size:12px;cursor:pointer;color:#fff;transition:transform .1s;background:#ef4444;box-shadow:0 2px 0 #dc2626}#Translation button.active{background:#22c55e;box-shadow:0 2px 0 #16a34a}#Translation button:first-of-type:not(.active)~div{opacity:.5}#Translation button:active{transform:scale(.97)}</style><button onclick="_translator.self.startStopTranslation(this)" style="margin-bottom:8px">OFF</button><div><label>RECV</label><div><select id="read" style="display:none"></select><select id="seen"></select></div></div><div><label>SEND</label><div><select id="send"></select></div></div><div><label>SMART MODE</label><button onclick="_translator.self.switchSmartMode(this)" class="active" style="width:unset">ON</button></div>`
                        });
                    document.body.appendChild(this.control);
                    this.control.querySelectorAll('select').forEach(q=>{q.innerHTML=Object.entries(this.langs).map(([k,v])=>`<option ${k===this[`${q.id}Language`]?'selected':''} value=${k}>${v}</option>`).join('')});
                    document.addEventListener('mousedown',e=>{!this.control.contains(e.target)&&e.target!==this.trigger&&(this.control.style.display='none')});
                    window.addEventListener('resize',()=>this.control.style.display=='block'&&this.syncPos());
                }
            }
        }
    });
})();