/* OpenTerminal 官网语言层 选定语言的优先级(与 index.html 头部内联脚本完全一致): 1. URL 参数 ?lang=xx 2. localStorage['openterminal.lang'] 3. navigator.language(s) 前缀匹配 4. 默认 zh-CN index.html 头部内联脚本先按同一优先级把 定好(首帧防闪), 本文件直接读 document.documentElement.lang 作为唯一事实源,不重复判定。 应用规则: - [data-i18n="k"] → textContent - [data-i18n-html="k"] → innerHTML(字典是本地静态文件,非用户输入) - [data-i18n-attr="alt:k;title:k2"] → 逐对写属性,分号分隔 缺键回落到 zh-CN;仍缺则保持 HTML 里的原文,不会把页面写空。 语言切换后派发 document 事件 'openterminal:langchange'(detail.lang), 页面尾部的内联脚本据此重刷主题按钮文案。 */ (function () { 'use strict'; var DICT = window.OPENTERMINAL_I18N || {}; var STORAGE_KEY = 'openterminal.lang'; var FALLBACK = 'zh-CN'; var SUPPORTED = ['zh-CN', 'zh-TW', 'en', 'ja']; function normalize(tag) { if (!tag) return null; var t = String(tag).replace(/_/g, '-'); if (/^zh-?(tw|hk|mo)/i.test(t)) return 'zh-TW'; // zh-Hant-* is Traditional by definition (kept in sync with the head script). if (/^zh-hant/i.test(t)) return 'zh-TW'; if (/^zh/i.test(t)) return 'zh-CN'; if (/^ja/i.test(t)) return 'ja'; if (/^en/i.test(t)) return 'en'; return null; } function tableOf(lang) { return DICT[lang] || DICT[FALLBACK] || {}; } function lookup(lang, key) { var table = tableOf(lang); if (Object.prototype.hasOwnProperty.call(table, key)) return table[key]; var fb = DICT[FALLBACK] || {}; if (Object.prototype.hasOwnProperty.call(fb, key)) return fb[key]; return null; } function applyAttrs(lang) { var nodes = document.querySelectorAll('[data-i18n-attr]'); for (var i = 0; i < nodes.length; i++) { var pairs = nodes[i].getAttribute('data-i18n-attr').split(';'); for (var j = 0; j < pairs.length; j++) { var parts = pairs[j].split(':'); if (parts.length < 2) continue; var attr = parts[0].trim(); var key = parts.slice(1).join(':').trim(); var val = lookup(lang, key); if (attr && val !== null) nodes[i].setAttribute(attr, val); } } } function applyText(lang) { var nodes = document.querySelectorAll('[data-i18n]'); for (var i = 0; i < nodes.length; i++) { var val = lookup(lang, nodes[i].getAttribute('data-i18n')); if (val !== null) nodes[i].textContent = val; } } function applyHtml(lang) { var nodes = document.querySelectorAll('[data-i18n-html]'); for (var i = 0; i < nodes.length; i++) { var val = lookup(lang, nodes[i].getAttribute('data-i18n-html')); if (val !== null) nodes[i].innerHTML = val; } } function clearPending() { document.documentElement.removeAttribute('data-i18n-pending'); } function apply(lang, announce) { var target = normalize(lang) || FALLBACK; try { document.documentElement.lang = target; applyText(target); applyHtml(target); applyAttrs(target); } finally { // 任何异常都不能让 body 一直藏在 visibility:hidden 后面 clearPending(); } var sel = document.getElementById('lang-select'); if (sel && sel.value !== target) sel.value = target; if (announce) { try { document.dispatchEvent(new CustomEvent('openterminal:langchange', { detail: { lang: target } })); } catch (e) { var ev = document.createEvent('Event'); ev.initEvent('openterminal:langchange', true, true); ev.detail = { lang: target }; document.dispatchEvent(ev); } } return target; } // 头部内联脚本已经定好语言;这里只是把它落到 DOM 上 var current = apply(document.documentElement.lang || FALLBACK, false); var select = document.getElementById('lang-select'); if (select) { select.value = current; select.addEventListener('change', function () { var next = normalize(select.value) || FALLBACK; try { localStorage.setItem(STORAGE_KEY, next); } catch (e) {} current = apply(next, true); }); } window.OPENTERMINAL_SET_LANG = function (lang) { current = apply(lang, true); try { localStorage.setItem(STORAGE_KEY, current); } catch (e) {} return current; }; window.OPENTERMINAL_I18N_SUPPORTED = SUPPORTED.slice(); })();