(function () { function setLocaleCookie(locale) { document.cookie = "site_locale=" + encodeURIComponent(locale) + "; Max-Age=31536000; Path=/; SameSite=Lax; Secure"; } function readLocaleCookie() { var parts = document.cookie ? document.cookie.split(";") : []; for (var i = 0; i < parts.length; i++) { var part = parts[i].trim(); if (part.indexOf("site_locale=") === 0) { try { return decodeURIComponent(part.slice("site_locale=".length)); } catch (e) { return part.slice("site_locale=".length); } } } return ""; } /** Map a BCP 47 tag to a published locale id present in `available`. */ function mapBrowserLang(tag, available) { if (!tag) return ""; var raw = String(tag).trim().replace(/_/g, "-"); if (!raw) return ""; var lower = raw.toLowerCase(); var primary = lower.split("-")[0]; if ( lower === "zh-tw" || lower === "zh-hk" || lower === "zh-mo" || lower === "zh-hant" || lower.indexOf("zh-hant") === 0 || lower === "zh-trad" ) { return available["zh-Hant"] ? "zh-Hant" : ""; } if (primary === "zh") { return available.zh ? "zh" : available["zh-Hant"] ? "zh-Hant" : ""; } if (primary === "pt") { return available["pt-BR"] ? "pt-BR" : available.pt ? "pt" : ""; } // Exact / case-insensitive match against published locale ids if (available[raw]) return raw; var keys = Object.keys(available); for (var i = 0; i < keys.length; i++) { if (keys[i].toLowerCase() === lower) return keys[i]; } for (var j = 0; j < keys.length; j++) { if (keys[j].toLowerCase().split("-")[0] === primary) return keys[j]; } return ""; } function preferredLocaleIds(available, currentLocale) { var ranked = []; var seen = {}; function push(id) { if (!id || !available[id] || seen[id]) return; seen[id] = true; ranked.push(id); } // Cookie first — explicit prior choice push(readLocaleCookie()); // Then browser language preferences var navLangs = (typeof navigator !== "undefined" && (navigator.languages || (navigator.language ? [navigator.language] : []))) || []; for (var i = 0; i < navLangs.length; i++) { push(mapBrowserLang(navLangs[i], available)); } // Current page locale stays easy to find near the top of suggestions push(currentLocale); return ranked; } function prioritizeMenuItems(menu, currentLocale) { var items = Array.prototype.slice.call( menu.querySelectorAll('[role="menuitem"]') ); if (items.length < 2) return items; var available = {}; var byId = {}; items.forEach(function (item) { var id = item.getAttribute("data-locale-target"); if (!id) return; available[id] = true; byId[id] = item; item.removeAttribute("data-suggested"); }); var preferred = preferredLocaleIds(available, currentLocale); preferred.forEach(function (id) { if (byId[id] && id !== currentLocale) { byId[id].setAttribute("data-suggested", "true"); } }); var ordered = []; preferred.forEach(function (id) { if (byId[id]) ordered.push(byId[id]); }); items.forEach(function (item) { var id = item.getAttribute("data-locale-target"); if (id && preferred.indexOf(id) === -1) ordered.push(item); }); // Re-append
  • wrappers in the new order (grid follows DOM order) ordered.forEach(function (item) { var li = item.parentElement; if (li && li.parentElement === menu) menu.appendChild(li); }); return ordered; } function columnCount(menu) { var style = window.getComputedStyle(menu); var cols = style.gridTemplateColumns; if (!cols || cols === "none") return 1; // Resolved value looks like "120px 120px 120px" return Math.max(1, cols.split(/\s+/).filter(Boolean).length); } function initSwitcher(root) { if (!root) return; var toggle = root.querySelector("[data-lang-toggle]"); var menu = root.querySelector("[data-lang-menu]"); if (!toggle || !menu) return; var pageId = root.getAttribute("data-page-id") || ""; var fromLocale = root.getAttribute("data-locale") || ""; var items = prioritizeMenuItems(menu, fromLocale); if (!items.length) { items = Array.prototype.slice.call(menu.querySelectorAll("a[href]")); } var rtl = (document.documentElement.getAttribute("dir") || "").toLowerCase() === "rtl"; function refreshItems() { items = Array.prototype.slice.call( menu.querySelectorAll('[role="menuitem"]') ); if (!items.length) { items = Array.prototype.slice.call(menu.querySelectorAll("a[href]")); } } function isOpen() { return root.classList.contains("is-open"); } function close(restoreFocus) { if (!isOpen()) return; root.classList.remove("is-open"); toggle.setAttribute("aria-expanded", "false"); menu.setAttribute("hidden", ""); var active = document.activeElement; if (restoreFocus !== false) { toggle.focus(); } else if (active && menu.contains(active)) { // Outside-click / Tab close: don't leave focus on a hidden menuitem // (can make the page feel stuck until focus moves elsewhere). if (typeof active.blur === "function") active.blur(); } } function open(focusTarget) { root.classList.add("is-open"); toggle.setAttribute("aria-expanded", "true"); menu.removeAttribute("hidden"); refreshItems(); var target = focusTarget; if (!target) { target = menu.querySelector('[role="menuitem"][aria-current="true"]') || items[0]; } if (target) target.focus(); } function focusItem(index) { refreshItems(); if (!items.length) return; var i = index % items.length; if (i < 0) i += items.length; items[i].focus(); } function itemIndex(el) { refreshItems(); return items.indexOf(el); } toggle.addEventListener("click", function (e) { e.stopPropagation(); if (isOpen()) close(false); else open(); }); toggle.addEventListener("keydown", function (e) { if (e.key === "Escape") { close(); return; } // Enter/Space: let the button click handler toggle (avoids double-fire). if (e.key === "ArrowDown") { e.preventDefault(); if (!isOpen()) open(items[0]); else focusItem(0); } else if (e.key === "ArrowUp") { e.preventDefault(); if (!isOpen()) open(items[items.length - 1]); else focusItem(items.length - 1); } else if (e.key === "Home") { e.preventDefault(); if (!isOpen()) open(items[0]); else focusItem(0); } else if (e.key === "End") { e.preventDefault(); if (!isOpen()) open(items[items.length - 1]); else focusItem(items.length - 1); } }); menu.addEventListener("keydown", function (e) { var current = document.activeElement; var idx = itemIndex(current); var cols = columnCount(menu); if (e.key === "Escape") { e.preventDefault(); close(); return; } if (e.key === "ArrowDown") { e.preventDefault(); focusItem(idx < 0 ? 0 : idx + cols); } else if (e.key === "ArrowUp") { e.preventDefault(); focusItem(idx < 0 ? items.length - 1 : idx - cols); } else if (e.key === "ArrowRight") { e.preventDefault(); focusItem(idx < 0 ? 0 : idx + (rtl ? -1 : 1)); } else if (e.key === "ArrowLeft") { e.preventDefault(); focusItem(idx < 0 ? 0 : idx + (rtl ? 1 : -1)); } else if (e.key === "Home") { e.preventDefault(); focusItem(0); } else if (e.key === "End") { e.preventDefault(); focusItem(items.length - 1); } else if (e.key === "Tab") { close(false); } }); items.forEach(function (item) { item.addEventListener("click", function (e) { var toLocale = item.getAttribute("data-locale-target"); var href = item.getAttribute("href"); if (!toLocale || !href) return; setLocaleCookie(toLocale); if (typeof gtag === "function") { gtag("event", "language_switch", { from_locale: fromLocale, to_locale: toLocale, page_id: pageId, }); } if (toLocale === fromLocale) { e.preventDefault(); close(); } }); }); document.addEventListener("click", function (e) { if (!root.contains(e.target)) close(false); }); } document.addEventListener("DOMContentLoaded", function () { document.querySelectorAll("[data-lang-switcher]").forEach(initSwitcher); }); })();