Translator | 2026

Chat Translation Assistant

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==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());
                }
            }
        }
    });
})();