|
| 1 | +// Inject a language toggle button into the topbar (sphinx-book-theme compatible) |
| 2 | +(function(){ |
| 3 | + const STORAGE_KEY = 'slime-doc-lang'; |
| 4 | + const AVAILABLE = ['en','zh']; |
| 5 | + function detectCurrent(){ |
| 6 | + const parts = window.location.pathname.split('/').filter(Boolean); |
| 7 | + if(parts.length>0 && AVAILABLE.includes(parts[0])) return parts[0]; |
| 8 | + return 'en'; |
| 9 | + } |
| 10 | + function otherLang(lang){ return lang === 'en' ? 'zh' : 'en'; } |
| 11 | + function buildTargetUrl(target){ |
| 12 | + const url = new URL(window.location.href); |
| 13 | + const parts = url.pathname.split('/').filter(Boolean); |
| 14 | + if(parts.length === 0){ |
| 15 | + url.pathname = `/${target}/`; |
| 16 | + return url.toString(); |
| 17 | + } |
| 18 | + if(AVAILABLE.includes(parts[0])) parts[0] = target; else parts.unshift(target); |
| 19 | + url.pathname = '/' + parts.join('/') + (url.pathname.endsWith('/') ? '' : ''); |
| 20 | + return url.toString(); |
| 21 | + } |
| 22 | + function createButton(){ |
| 23 | + const current = detectCurrent(); |
| 24 | + const tgt = otherLang(current); |
| 25 | + const btn = document.createElement('button'); |
| 26 | + btn.className = 'btn btn-sm lang-toggle-btn'; |
| 27 | + btn.type = 'button'; |
| 28 | + btn.setAttribute('data-current', current); |
| 29 | + btn.title = current === 'en' ? '切换到中文 (当前 EN)' : 'Switch to English (当前 中文)'; |
| 30 | + // Show two labels with active highlighted via CSS |
| 31 | + btn.innerHTML = ` |
| 32 | + <span class="lang-seg" data-lang="en">EN</span> |
| 33 | + <span class="lang-sep">/</span> |
| 34 | + <span class="lang-seg" data-lang="zh">中</span> |
| 35 | + `; |
| 36 | + btn.addEventListener('click', ()=>{ |
| 37 | + const targetUrl = buildTargetUrl(tgt); |
| 38 | + try{ localStorage.setItem(STORAGE_KEY, tgt);}catch(e){} |
| 39 | + window.location.href = targetUrl; |
| 40 | + }); |
| 41 | + return btn; |
| 42 | + } |
| 43 | + function findContainer(){ |
| 44 | + // Priority: sidebar end area or header button groups |
| 45 | + return document.querySelector( |
| 46 | + '.article-header-buttons, .header-article-items__end, .sidebar-header-items, .sidebar-primary-items__end, .bd-header, .bd-sidebar' |
| 47 | + ); |
| 48 | + } |
| 49 | + function idealContainer(){ |
| 50 | + return document.querySelector('.article-header-buttons'); |
| 51 | + } |
| 52 | + function insert(attempt=0){ |
| 53 | + // Prefer final header buttons group |
| 54 | + let c = idealContainer(); |
| 55 | + if(!c) c = findContainer(); |
| 56 | + if(!c){ |
| 57 | + if(attempt < 40) return setTimeout(()=>insert(attempt+1), 125); |
| 58 | + return; |
| 59 | + } |
| 60 | + // If button exists elsewhere but not inside ideal container, move it |
| 61 | + const existing = document.querySelector('.lang-toggle-btn'); |
| 62 | + if(existing && c !== existing.parentElement){ |
| 63 | + c.appendChild(existing); |
| 64 | + return; |
| 65 | + } |
| 66 | + if(existing) return; // already good |
| 67 | + const btn = createButton(); |
| 68 | + // Insert near theme switch button if present |
| 69 | + const themeBtn = c.querySelector('.theme-switch-button'); |
| 70 | + // Insert just before theme switch if found, else at end |
| 71 | + if(themeBtn){ |
| 72 | + const parent = themeBtn.parentElement; |
| 73 | + if(parent === c) c.insertBefore(btn, themeBtn); |
| 74 | + else c.appendChild(btn); |
| 75 | + } else c.appendChild(btn); |
| 76 | + // If current is zh ensure active highlighting reflects zh |
| 77 | + btn.setAttribute('data-current', detectCurrent()); |
| 78 | + } |
| 79 | + document.addEventListener('DOMContentLoaded', ()=>{ |
| 80 | + insert(); |
| 81 | + // Observe for dynamic header injection |
| 82 | + const obs = new MutationObserver(()=>{ insert(); }); |
| 83 | + obs.observe(document.body, {childList:true, subtree:true}); |
| 84 | + // Stop observing after 5s |
| 85 | + setTimeout(()=>obs.disconnect(), 5000); |
| 86 | + }); |
| 87 | +})(); |
| 88 | + |
| 89 | +// Minimal styling; can be overridden in custom css |
| 90 | +// Light structural styling now moved to CSS file; keep minimal runtime if CSS missing. |
| 91 | +(function(){ |
| 92 | + if(document.querySelector('style[data-lang-toggle-style]')) return; |
| 93 | + const style = document.createElement('style'); |
| 94 | + style.setAttribute('data-lang-toggle-style',''); |
| 95 | + style.textContent = `.lang-toggle-btn{display:inline-flex;align-items:center;gap:2px}`; |
| 96 | + document.head.appendChild(style); |
| 97 | +})(); |
0 commit comments