您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Load translations for Online Math Contest problems and editorials. / OMCの問題文および公式解説文の翻訳を表示します。
当前为
- // ==UserScript==
- // @name OMC Translator
- // @namespace https://github.com/yuyuuuuuuuuuuuu/omc-translations
- // @version 1.0.0
- // @description Load translations for Online Math Contest problems and editorials. / OMCの問題文および公式解説文の翻訳を表示します。
- // @author yuyuuuuuuuuuuuu
- // @match https://onlinemathcontest.com/*
- // @grant none
- // @homepageURL https://github.com/yuyuuuuuuuuuuuu/omc-translations
- // @license MIT
- // ==/UserScript==
- (function() {
- 'use strict';
- // 設定
- const GITHUB_USER = 'yuyuuuuuuuuuuuu',
- REPO_NAME = 'omc-translations',
- BRANCH = 'main';
- // 対応言語リスト (code: 言語コード, label: ハンバーグ表示)
- const LANGUAGES = [
- { code: 'en', label: 'English 🇺🇸' },
- { code: 'ja', label: '日本語 🇯🇵' }
- // 追加例(誰か2ドルくれ): { code: 'zh', label: '中文 🇨🇳' }
- ];
- // デフォルト 'en'
- const getLang = () => {
- const v = localStorage.getItem('omcLang');
- return LANGUAGES.some(l => l.code === v) ? v : 'en';
- };
- const setLang = code => localStorage.setItem('omcLang', code);
- // URL から contestId/taskId を抽出 (type: 'tasks' or 'editorial')
- const parseInfo = type => {
- const re = new RegExp(`^/contests/([^/]+)/(?:${type})/(\\d+)(?:/|$)`);
- const m = location.pathname.match(re);
- return m ? { contestId: m[1], taskId: m[2] } : null;
- };
- // GitHub raw URL を生成(type: 'tasks' or 'editorial', lang, contestId, taskId)
- const rawUrl = (type, lang, c, t) =>
- `https://raw.githubusercontent.com/${GITHUB_USER}/${REPO_NAME}/${BRANCH}` +
- `/languages/${lang}/contests/${c}/${type}/${t}.html`;
- // 言語ハンバーグをヘッダーに追加
- function addLangDropdown() {
- const ul = document.querySelector('.navbar-nav.mr-auto');
- if (!ul) return;
- const current = getLang();
- const li = document.createElement('li');
- li.className = 'nav-item dropdown';
- li.style.marginLeft = '10px';
- const toggle = document.createElement('a');
- toggle.className = 'nav-link dropdown-toggle';
- toggle.href = '#';
- toggle.id = 'langDropdown';
- toggle.setAttribute('role', 'button');
- toggle.setAttribute('data-toggle', 'dropdown');
- toggle.textContent = `Language: ${LANGUAGES.find(l => l.code === current).label}`;
- const menu = document.createElement('div');
- menu.className = 'dropdown-menu';
- menu.setAttribute('aria-labelledby', 'langDropdown');
- LANGUAGES.forEach(l => {
- const a = document.createElement('a');
- a.className = 'dropdown-item';
- a.href = '#';
- a.dataset.code = l.code;
- a.textContent = l.label;
- if (l.code === current) a.style.fontWeight = 'bold';
- a.addEventListener('click', e => {
- e.preventDefault();
- setLang(l.code);
- toggle.textContent = `Language: ${l.label}`;
- menu.classList.remove('show');
- location.reload();
- });
- menu.appendChild(a);
- });
- li.appendChild(toggle);
- li.appendChild(menu);
- ul.appendChild(li);
- }
- //('problem_content' or 'editorial_content') 置き換え
- function replaceContent(type) {
- const info = parseInfo(type);
- if (!info || getLang() === 'ja') return;
- const { contestId, taskId } = info;
- const url = rawUrl(type, getLang(), contestId, taskId);
- fetch(url)
- .then(res => {
- if (!res.ok) throw new Error('not found');
- return res.text();
- })
- .then(html => {
- const sel = type === 'tasks' ? 'problem_content' : 'editorial_content';
- const container = document.getElementById(sel);
- if (container) container.innerHTML = html;
- })
- .catch(() => {
- const sel = type === 'tasks' ? 'problem_content' : 'editorial_content';
- const c = document.getElementById(sel);
- if (c) {
- const p = document.createElement('p');
- p.textContent = "It seems the translation hasn't been completed yet... Please wait a little longer...";
- p.style.color = 'red';
- p.style.marginTop = '1em';
- c.appendChild(p);
- }
- });
- }
- // 実行
- const main = () => {
- addLangDropdown();
- replaceContent('tasks');
- replaceContent('editorial');
- };
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', main);
- } else {
- main();
- }
- })();