您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Translate BA Torment to English
// ==UserScript== // @name BA Torment Translate // @namespace bluearchive-torment // @match https://bluearchive-torment.netlify.app/* // @grant none // @version 1.1 // @author u/aisjsjdjdjskwkw // @license MIT // @description Translate BA Torment to English // ==/UserScript== // Full strings to replace const strings = { "총력전 리포트 (ARONA.AI)": "Raid Report (Arona.AI)", "파티 찾기": "Search", "파티 Filter": "Team Filters", "필터 Reset": "Reset Filters", "난이도": "Difficulty", "파티": " Team", "포함할 ": "Include", "내 캐릭터": " Students", "캐릭터를 선택하세요": "Select a student", "※ 성급 관계없이 보고 싶다면 왼쪽 체크박스를 사용하세요.": "※ To include a student regardless of star level, use the checkbox on the left.", "제외할 ": "Exclude", "제외할 캐릭터를 선택하세요": "Select a student to exclude", "조력자에서도 제외": "Exclude from assistants", "조력자": "Assistant", "조력자를 선택하세요": "Select an assistant", "조력자 포함 중복 허용": "Allow duplicates", "Youtube 링크 (beta)": "Youtube link (beta)", "위: ": (e) => { // Can't replace parentElement.textContent because the rank and score // text nodes need to exist as they're updated when changing page const rank = e.previousSibling const score = e.nextSibling const divider = score.nextSibling e.after(rank) divider.after(score) divider.textContent = " | " return "Rank " }, "채널": "Channel", "영상": "Video", "서비스 안내": "Service Information", "이제 영상 정보 업데이트를 지원하지 않아요.": "Submitting video links is no longer supported.", "기존 데이터는 계속 조회할 수 있어요.": "Existing links can still be accessed.", "5파티 이후 보기": "Show more teams", "요약": "Summary (Torment)", "요약 (루나틱)": "Summary (Lunatic)", "검색어 (클릭하면 복사됩니다)": "Search Term (click to copy)", "Platinum 클리어 비율: ": "Platinum Clears: ", "Top 5 파티": "Top 5 Most Common Clears", "※ 전용무기와 배치는 고려하지 않았습니다.": "※ Without considering student star level or positioning.", "파티 비율": "Team Count Usage", "1파티": "1 team", "2파티": "2 teams", "3파티": "3 teams", "4파티 이상": "4 or more teams", "캐릭터 사용률": "Student Usage", "※ 1% 이상만 표시됩니다.": "※ Only students with at least 1% usage are displayed.", "이름": "Name", "사용률 (%)": "Usage Rate (%)", "캐릭터 성장 통계": "Student Investment Statistics", "공략 영상": "Clear Videos", "※ 최대 10개까지 표시됩니다.": "※ Up to 10 are displayed.", "오류 제보 (이메일)": "Bug Report (Email)", } // Words to replace within strings const words = [ ["총력전", "TA"], ["대결전", "GA"], ["비나", "Binah"], ["헤세드", "Chesed"], ["시로쿠로", "ShiroKuro"], ["예로니무스", "Hieronymus"], ["카이텐", "Kaiten"], ["페로로지라", "Perorodzilla"], ["호드", "Hod"], ["고즈", "Goz"], ["그레고리오", "Gregorius"], ["호버크래프트", "Hovercraft"], ["쿠로카게", "Kurokage"], ["게부라", "Geburah"], ["실내", "Indoors"], ["야외", "Outdoors"], ["시가지", "Urban"], ["경장갑", "Red"], ["중장갑", "Yellow"], ["특수장갑", "Blue"], ["탄력장갑", "Purple"], ["인세인", "Insane"], ["토먼트", "Torment"], ["루나틱", "Lunatic"], ["총합", "Total"], [/\d{8}/, (e) => Number(e.textContent).toLocaleString()], [/최종 (\d+)위/, "Overall Rank $1"], [/(\d+)위: (\d+)명 사용/, "#$1 | $2 uses"], [/^in (\d+)$/, "Top $1"], ["1성", "1★"], ["2성", "2★"], ["3성", "3★"], ["4성", "4★"], ["5성", "5★"], ["전1", "UE1"], ["전2", "UE2"], ["전3", "UE3"], ["전4", "UE4"], ["전5", "UE5"], ["아","a"], ["바","ba"], ["비","bi"], ["부","bu"], ["체","che"], ["치","chi"], ["데","de"], ["도","do"], ["에","e"], ["후","fu"], ["게","ge"], ["기","gi"], ["구","gu"], ["하","ha"], ["히","hi"], ["호","ho"], ["이","i"], ["지","ji"], ["죠","jo"], ["주","ju"], ["준","jun"], ["카","ka"], ["칸","kan"], ["키","ki"], ["코","ko"], ["쿠","ku"], ["쿄","kyou"], ["마","ma"], ["메","me"], ["미","mi"], ["모","mo"], ["무","mu"], ["나","na"], ["네","ne"], ["니","ni"], ["노","no"], ["오","o"], ["피","pi"], ["라","ra"], ["레","re"], ["렌","ren"], ["리","ri"], ["린","rin"], ["로","ro"], ["루","ru"], ["사","sa"], ["세","se"], ["시","shi"], ["쇼","shou"], ["스","su"], ["타","ta"], ["테","te"], ["텐","ten"], ["토","to"], ["츠","tsu"], ["우","u"], ["와","wa"], ["야","ya"], ["요","yo"], ["유","yu"], ["조","zo"], ["즈","zu"], ["아루", "Aru"], ["에이미", "Eimi"], ["하루나", "Haruna"], ["히후미", "Hifumi"], ["히나", "Hina"], ["호시노", "Hoshino"], ["이오리", "Iori"], ["마키", "Maki"], ["네루", "Neru"], ["이즈미", "Izumi"], ["시로코", "Shiroko"], ["슌", "Shun"], ["스미레", "Sumire"], ["츠루기", "Tsurugi"], ["아카네", "Akane"], ["치세", "Chise"], ["아카리", "Akari"], ["하스미", "Hasumi"], ["노노미", "Nonomi"], ["카요코", "Kayoko"], ["무츠키", "Mutsuki"], ["준코", "Junko"], ["세리카", "Serika"], ["츠바키", "Tsubaki"], ["유우카", "Yuuka"], ["하루카", "Haruka"], ["아스나", "Asuna"], ["코토리", "Kotori"], ["스즈미", "Suzumi"], ["피나", "Pina"], ["히비키", "Hibiki"], ["카린", "Karin"], ["사야", "Saya"], ["아이리", "Airi"], ["후우카", "Fuuka"], ["하나에", "Hanae"], ["하레", "Hare"], ["우타하", "Utaha"], ["아야네", "Ayane"], ["치나츠", "Chinatsu"], ["코타마", "Kotama"], ["주리", "Juri"], ["세리나", "Serina"], ["시미코", "Shimiko"], ["요시미", "Yoshimi"], ["마시로", "Mashiro"], ["이즈나", "Izuna"], ["시즈코", "Shizuko"], ["아리스", "Arisu"], ["미도리", "Midori"], ["모모이", "Momoi"], ["체리노", "Cherino"], ["노도카", "Nodoka"], ["유즈", "Yuzu"], ["아즈사", "Azusa"], ["하나코", "Hanako"], ["코하루", "Koharu"], ["아즈사(수영복)", "S.Azusa"], ["마시로(수영복)", "S.Mashiro"], ["츠루기(수영복)", "S.Tsurugi"], ["히후미(수영복)", "S.Hifumi"], ["히나(수영복)", "S.Hina"], ["이오리(수영복)", "S.Iori"], ["이즈미(수영복)", "S.Izumi"], ["시로코(라이딩)", "C.Shiroko"], ["슌(어린이)", "Shun (Small)"], ["키리노", "Kirino"], ["사야(사복)", "C.Saya"], ["네루(바니걸)", "B.Neru"], ["카린(바니걸)", "B.Karin"], ["아스나(바니걸)", "B.Asuna"], ["나츠", "Natsu"], ["마리", "Mari"], ["하츠네 미쿠", "Hatsune Miku"], ["아코", "Ako"], ["체리노(온천)", "O.Cherino"], ["치나츠(온천)", "O.Chinatsu"], ["토모에", "Tomoe"], ["노도카(온천)", "O.Nodoka"], ["아루(새해)", "NY.Aru"], ["무츠키(새해)", "NY.Mutsuki"], ["세리카(새해)", "NY.Serika"], ["와카모", "Wakamo"], ["후부키", "Fubuki"], ["세나", "Sena"], ["치히로", "Chihiro"], ["미모리", "Mimori"], ["우이", "Ui"], ["히나타", "Hinata"], ["마리나", "Marina"], ["미야코", "Miyako"], ["사키", "Saki"], ["미유", "Miyu"], ["카에데", "Kaede"], ["이로하", "Iroha"], ["미치루", "Michiru"], ["츠쿠요", "Tsukuyo"], ["미사키", "Misaki"], ["히요리", "Hiyori"], ["아츠코", "Atsuko"], ["와카모(수영복)", "S.Wakamo"], ["노노미(수영복)", "S.Nonomi"], ["아야네(수영복)", "S.Ayane"], ["호시노(수영복)", "S.Hoshino"], ["시즈코(수영복)", "S.Shizuko"], ["이즈나(수영복)", "S.Izuna"], ["치세(수영복)", "S.Chise"], ["사오리", "Saori"], ["모에", "Moe"], ["카즈사", "Kazusa"], ["코코나", "Kokona"], ["우타하(응원단)", "C.Utaha"], ["노아", "Noa"], ["히비키(응원단)", "C.Hibiki"], ["아카네(바니걸)", "B.Akane"], ["유우카(체육복)", "T.Yuuka"], ["마리(체육복)", "T.Mari"], ["하스미(체육복)", "T.Hasumi"], ["히마리", "Himari"], ["시구레", "Shigure"], ["세리나(크리스마스)", "C.Serina"], ["하나에(크리스마스)", "C.Hanae"], ["하루나(새해)", "NY.Haruna"], ["후우카(새해)", "NY.Fuuka"], ["준코(새해)", "NY.Junko"], ["미네", "Mine"], ["미카", "Mika"], ["메구", "Megu"], ["칸나", "Kanna"], ["사쿠라코", "Sakurako"], ["토키", "Toki"], ["나기사", "Nagisa"], ["코유키", "Koyuki"], ["카요코(새해)", "NY.Kayoko"], ["하루카(새해)", "NY.Haruka"], ["카호", "Kaho"], ["아리스(메이드)", "M.Arisu"], ["토키(바니걸)", "B.Toki"], ["유즈(메이드)", "M.Yuzu"], ["레이사", "Reisa"], ["루미", "Rumi"], ["미나", "Mina"], ["미노리", "Minori"], ["미야코(수영복)", "S.Miyako"], ["사키(수영복)", "S.Saki"], ["미유(수영복)", "S.Miyu"], ["시로코(수영복)", "S.Shiroko"], ["우이(수영복)", "S.Ui"], ["히나타(수영복)", "S.Hinata"], ["코하루(수영복)", "S.Koharu"], ["하나코(수영복)", "S.Hanako"], ["미모리(수영복)", "S.Mimori"], ["메루", "Meru"], ["모미지", "Momiji"], ["코토리(응원단)", "C.Kotori"], ["하루나(체육복)", "T.Haruna"], ["이치카", "Ichika"], ["카스미", "Kasumi"], ["시구레(온천)", "O.Shigure"], ["미사카 미코토", "Misaka Mikoto"], ["쇼쿠호 미사키", "Shokuhou Misaki"], ["사텐 루이코", "Saten Ruiko"], ["유카리", "Yukari"], ["렌게", "Renge"], ["키쿄", "Kikyou"], ["에이미(수영복)", "S.Eimi"], ["코타마(캠핑)", "C.Kotama"], ["하레(캠핑)", "C.Hare"], ["아코(드레스)", "D.Ako"], ["이부키", "Ibuki"], ["마코토", "Makoto"], ["히나(드레스)", "D.Hina"], ["카요코(드레스)", "D.Kayoko"], ["아루(드레스)", "D.Aru"], ["아카리(새해)", "NY.Akari"], ["우미카", "Umika"], ["츠바키(가이드)", "G.Tsubaki"], ["카즈사(밴드)", "B.Kazusa"], ["요시미(밴드)", "B.Yoshimi"], ["아이리(밴드)", "B.Airi"], ["키라라", "Kirara"], ["모모이(메이드)", "M.Momoi"], ["미도리(메이드)", "M.Midori"], ["세리카(수영복)", "S.Serika"], ["칸나(수영복)", "S.Kanna"], ["후부키(수영복)", "S.Fubuki"], ["키리노(수영복)", "S.Kirino"], ["모에(수영복)", "S.Moe"], ["호시노(무장)", "B.Hoshino"], ["시로코*테러", "Kuroko"], ["아츠코(수영복)", "S.Atsuko"], ["사오리(수영복)", "S.Saori"], ["히요리(수영복)", "S.Hiyori"], ["마리나(치파오)", "Q.Marina"], ["토모에(치파오)", "Q.Tomoe"], ["레이죠", "Reijo"], ["키사키", "Kisaki"], ["마리(아이돌)", "I.Mari"], ["사쿠라코(아이돌)", "I.Sakurako"], ["미네(아이돌)", "I.Mine"], ["치아키", "Chiaki"], ["사츠키", "Satsuki"], ["유우카(파자마)", "PJ.Yuuka"], ["노아(파자마)", "PJ.Noa"], ["세이아", "Seia"], ["아스나(교복)", "U.Asuna"], ["카린(교복)", "U.Karin"], ["네루(교복)", "U.Neru"], ["리오", "Rio"], ["마키(캠핑)", "C.Maki"], ["세나(사복)", "C.Sena"], ["주리(아르바이트)", "PT.Juri"], ["이즈미(새해)", "NY.Izumi"], ["레이", "Rei"], ["스미레(아르바이트)", "PT.Sumire"], ["사오리(드레스)", "D.Saori"], ["히카리", "Hikari"], ["노조미", "Nozomi"], ["아오바", "Aoba"], ["피나(가이드)", "G.Pina"], ["나구사", "Nagusa"], ["니야", "Niya"], ["나츠(밴드)", "B.Natsu"], ["유카리(수영복)", "S.Yukari"], ["렌게(수영복)", "S.Renge"], ["키쿄(수영복)", "S.Kikyou"], ["세이아(수영복)", "S.Seia"], ["하스미(수영복)", "S.Hasumi"], ["이치카(수영복)", "S.Ichika"], ["무장", "Armed"], ["밴드", "Band"], ["바니걸", "Bunny"], ["캠핑", "Camp"], ["사복", "Casual"], ["응원단", "Cheer Squad"], ["크리스마스", "Christmas"], ["라이딩", "Cycling"], ["드레스", "Dress"], ["가이드", "Guide"], ["온천", "Hot Spring"], ["메이드", "Maid"], ["새해", "New Year"], ["아르바이트", "Part-time Job"], ["파자마", "Pajamas"], ["아이돌", "Pop Idol"], ["치파오", "Qipao"], ["교복", "School"], ["어린이", "Small"], ["수영복", "Swimsuit"], ["러", "Terror"], ["체육복", "Track"], ].sort((a, b) => a[0].length < b[0].length) // Longest words first to avoid erroneously replacing substrings // DOM transformations // [css selector]: (e: Element) => {} const transforms = { // Student search boxes ".ant-select-selection-search > input": (e) => transliteratify(e), } // Romanised sounds to Hangul const transliterations = [ // Kana (this was generated by ChatGPT so I don't know how accurate it is) ["a", "아"], ["i", "이"], ["u", "우"], ["e", "에"], ["o", "오"], ["ka", "카"], ["ki", "키"], ["ku", "쿠"], ["ke", "케"], ["ko", "코"], ["ga", "가"], ["gi", "기"], ["gu", "구"], ["ge", "게"], ["go", "고"], ["sa", "사"], ["shi", "시"], ["su", "스"], ["se", "세"], ["so", "소"], ["ja", "자"], ["ji", "지"], ["ju", "주"], ["zo", "조"], ["ta", "타"], ["chi", "치"], ["tsu", "츠"], ["te", "테"], ["to", "토"], ["da", "다"], ["di", "디"], ["zu", "즈"], ["de", "데"], ["do", "도"], ["na", "나"], ["ni", "니"], ["nu", "누"], ["ne", "네"], ["no", "노"], ["ha", "하"], ["hi", "히"], ["fu", "후"], ["he", "헤"], ["ho", "호"], ["ba", "바"], ["bi", "비"], ["bu", "부"], ["be", "베"], ["bo", "보"], ["pa", "파"], ["pi", "피"], ["pu", "푸"], ["pe", "페"], ["po", "포"], ["ma", "마"], ["mi", "미"], ["mu", "무"], ["me", "메"], ["mo", "모"], ["ya", "야"], ["yu", "유"], ["yo", "요"], ["ra", "라"], ["ri", "리"], ["ru", "루"], ["re", "레"], ["ro", "로"], ["wa", "와"], ["wo", "오"], ["nn", "ㄴ"], ["kyo", "쿄"], ["sho", "쇼"], // Students ["che", "체"], // [che]rino ["주n", "준"], // [jun]ko ["카ㄴ아", "칸나"], // kanna ["테n", "텐"], // sa[ten] ["레nge", "렌게"], // renge ["쿠로코", "시로코*테러"], // kuroko ["shun", "슌"], // Alts ["(아rmed|바ttle)", "무장"], // armed/battle ["바nd", "밴드"], // band ["(부ㄴy|바니)", "바니걸"], // bunny ["camp", "캠핑"], ["casual", "사복"], ["체에r", "응원단"], // cheer ["(christmas|x)", "크리스마스"], ["(cy|(리|라이)디ng)", "라이딩"], // cycling/riding ["dress", "드레스"], ["구이데", "가이드"], // guide ["(hs|호t ?spring)", "온천"], // hot spring ["이도l", "아이돌"], // idol ["(마|메)이d", "메이드"], // maid ["(ny|네w ?year)", "새해"], // new year ["pj", "파자마"], // pajama ["(pt|파rt ?time)", "아르바이트"], // part time ["q", "치파오"], // qipao ["(school|jk|우니form)", "교복"], // school ["small", "어린이"], ["swim", "수영복"], // swimsuit ["테rror", "러"], // terror ["track", "체육복"], ].map(h => [ new RegExp("(^|[^a-z])" + h[0], "gi"), "$1" + h[1] ]) const observer = new MutationObserver(ms => ms.forEach(m => m.addedNodes.forEach(translateTree))) observer.observe(document.body, { childList: true, subtree: true }) translateTree(document.body) function translateTree(root) { switch (root.nodeType) { case Node.ELEMENT_NODE: transformNode(root) break case Node.TEXT_NODE: translate(root) break } const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT) while (walker.nextNode()) { const node = walker.currentNode if (node.nodeType === Node.TEXT_NODE) { new MutationObserver(() => translate(node)).observe(node, { characterData: true }) // console.log(`"${node.textContent}" --> "${translate(node)}"`, node) translate(node) continue } transformNode(node) } } function translate(text) { // Ignore announcement banner if (text.parentElement.closest(".ant-alert")) return let content = text.textContent const translation = strings[content] if (translation !== undefined) { return text.textContent = typeof translation === "function" ? translation(text) : translation } for (const [pattern, translation] of words) { if (typeof pattern === "string" ? content.includes(pattern) : content.match(pattern)) { content = typeof translation === "function" ? translation(text) : content.replace(pattern, translation) } } if (text.textContent !== content) { return text.textContent = content } return null } function transformNode(node) { for (const [css, transform] of Object.entries(transforms)) { if (node.matches(css)) { transform(node) return } } } function transliteratify(input) { const { onChange } = input[Object.keys(input).find(k => k.startsWith("__reactProps$"))] input.oninput = (e) => { const { value } = input if (value === "" || e.data === null) return let transliterated = value for (const [r, h] of transliterations) { transliterated = transliterated.replaceAll(r, h) } input.value = transliterated onChange({ target: { value: transliterated } }) } }