112 lines
5.5 KiB
JavaScript
112 lines
5.5 KiB
JavaScript
/* Optional landing-page languages. Remove this file, its script tag, and the
|
|
es/fr/zh/ru/ar folders to retire the feature. No shared-site dependency. */
|
|
(() => {
|
|
const script = document.currentScript;
|
|
if (!script) return;
|
|
const base = new URL('.', script.src);
|
|
const languages = [
|
|
['en', '🇬🇧', 'English', 'Choose language'],
|
|
['es', '🇪🇸', 'Español', 'Elegir idioma'],
|
|
['fr', '🇫🇷', 'Français', 'Choisir la langue'],
|
|
['zh', '🇨🇳', '中文', '选择语言'],
|
|
['ru', '🇷🇺', 'Русский', 'Выбрать язык'],
|
|
['ar', '🇸🇦', 'العربية', 'اختر اللغة'],
|
|
];
|
|
const codes = languages.map(([code]) => code);
|
|
const relative = location.pathname.startsWith(base.pathname)
|
|
? location.pathname.slice(base.pathname.length) : null;
|
|
const current = codes.includes(relative?.split('/')[0]) ? relative.split('/')[0] : 'en';
|
|
const key = `archivebox-language:${base.pathname}`;
|
|
const target = code => {
|
|
const url = new URL(code === 'en' ? './' : `${code}/`, base);
|
|
url.search = location.search;
|
|
url.searchParams.delete('lang');
|
|
url.hash = location.hash;
|
|
return url;
|
|
};
|
|
// Explicit language URLs always win; only the original entry page detects.
|
|
if (relative === '' || relative === 'index.html') {
|
|
let saved;
|
|
try { saved = localStorage.getItem(key); } catch { /* Storage is optional. */ }
|
|
const detected = (navigator.languages || [navigator.language])
|
|
.map(language => language.toLowerCase().split('-')[0])
|
|
.find(language => codes.includes(language));
|
|
const requested = new URL(location.href).searchParams.get('lang');
|
|
const preferred = codes.includes(requested) ? requested : codes.includes(saved) ? saved : detected || 'en';
|
|
if (preferred !== 'en') {
|
|
location.replace(target(preferred));
|
|
return;
|
|
}
|
|
}
|
|
const active = languages.find(([code]) => code === current);
|
|
const style = document.createElement('style');
|
|
style.textContent = `
|
|
.abx-language{position:relative;flex:0 0 auto;direction:ltr;font:13px/1.4 system-ui,sans-serif;color:#4d424b;letter-spacing:normal}
|
|
.abx-language>summary{display:flex;align-items:center;justify-content:center;gap:5px;min-width:52px;min-height:36px;padding:4px;cursor:pointer;list-style:none;border-radius:6px;white-space:nowrap}
|
|
.abx-language>summary::-webkit-details-marker{display:none}
|
|
.abx-language>summary:hover{background:#f4e8ed}
|
|
.abx-language>summary:focus-visible,.abx-language a:focus-visible{outline:2px solid #9b2854;outline-offset:2px}
|
|
.abx-language .abx-language-flag{font-size:18px;line-height:1}
|
|
.abx-language .abx-language-options{position:absolute;right:0;top:100%;z-index:1100;min-width:160px;padding:6px;background:#fcfaf7;color:#28252a;border:1px solid #ded4d8;border-radius:8px;box-shadow:0 6px 24px #28252a20;text-align:left}
|
|
.abx-language .abx-language-options a{display:flex;gap:9px;align-items:center;margin:0;padding:8px 10px;border-radius:4px;color:#4d424b;text-decoration:none;font:14px/1.4 system-ui,sans-serif;white-space:nowrap}
|
|
.abx-language .abx-language-options a:hover,.abx-language a[aria-current=page]{background:#f4e8ed}
|
|
.abx-language-fallback{position:fixed;right:12px;top:10px;z-index:1000;background:#fcfaf7;border:1px solid #ded4d8;border-radius:8px}
|
|
html[dir=rtl] .abx-header{direction:ltr}
|
|
html[dir=rtl] main{direction:rtl;text-align:right}
|
|
html[dir=rtl] pre,html[dir=rtl] code{direction:ltr;unicode-bidi:isolate}
|
|
`;
|
|
document.head.append(style);
|
|
const menu = document.createElement('details');
|
|
menu.className = 'abx-language';
|
|
const summary = document.createElement('summary');
|
|
summary.setAttribute('aria-label', `${active[3]}: ${active[2]}`);
|
|
summary.title = active[3];
|
|
const flag = document.createElement('span');
|
|
flag.className = 'abx-language-flag';
|
|
flag.setAttribute('aria-hidden', 'true');
|
|
flag.textContent = active[1];
|
|
const codeLabel = document.createElement('span');
|
|
codeLabel.textContent = current.toUpperCase();
|
|
summary.append(flag, codeLabel);
|
|
const options = document.createElement('div');
|
|
options.className = 'abx-language-options';
|
|
for (const [code, emoji, name] of languages) {
|
|
const link = document.createElement('a');
|
|
link.href = target(code).href;
|
|
link.lang = code;
|
|
link.hreflang = code;
|
|
if (code === current) link.setAttribute('aria-current', 'page');
|
|
link.textContent = `${emoji} ${name}`;
|
|
link.addEventListener('click', () => {
|
|
try { localStorage.setItem(key, code); } catch {
|
|
const url = target(code);
|
|
url.searchParams.set('lang', code);
|
|
link.href = url.href;
|
|
}
|
|
});
|
|
options.append(link);
|
|
}
|
|
menu.append(summary, options);
|
|
const fitMenu = () => {
|
|
if (!menu.open) return;
|
|
options.style.transform = '';
|
|
const bounds = options.getBoundingClientRect();
|
|
const shift = Math.max(8 - bounds.left, Math.min(0, innerWidth - 8 - bounds.right));
|
|
options.style.transform = `translateX(${shift}px)`;
|
|
};
|
|
summary.addEventListener('click', event => {
|
|
event.preventDefault();
|
|
menu.open = !menu.open;
|
|
fitMenu();
|
|
});
|
|
menu.addEventListener('toggle', fitMenu);
|
|
window.addEventListener('resize', fitMenu);
|
|
const nav = document.querySelector('.abx-header .abx-nav');
|
|
if (nav) nav.append(menu);
|
|
else { menu.classList.add('abx-language-fallback'); document.body.append(menu); }
|
|
document.addEventListener('click', event => { if (!menu.contains(event.target)) menu.open = false; });
|
|
menu.addEventListener('keydown', event => {
|
|
if (event.key === 'Escape') { menu.open = false; summary.focus(); }
|
|
});
|
|
})();
|