Translator | 2026

Chat Translation Assistant

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 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());
                }
            }
        }
    });
})();