1/** 2 * Iframe Language Parameter Handler 3 * 4 * This script detects when Google Translate changes the page language 5 * and appends the appropriate language query parameter to iframe URLs. 6 * 7 * - Spanish (es): appends ?lang=es 8 * - Chinese Simplified (zh-CN): appends ?lang=zh-CN 9 */ 10 11(function() { 12 'use strict'; 13 14 /** 15 * Get current language from hostname, Google Translate cookie, or HTML lang attribute 16 * @returns {string|null} Language code or null if default language 17 */ 18 function getCurrentLanguage() { 19 // Check hostname for subdomain-based translation (e.g., es.3cenergy.org) 20 var hostname = window.location.hostname.toLowerCase(); 21 if (hostname.startsWith('es.')) { 22 return 'es'; 23 } 24 if (hostname.startsWith('zh.') || hostname.startsWith('zh-cn.')) { 25 return 'zh-CN'; 26 } 27 28 // Check Google Translate cookie 29 var cookieMatch = document.cookie.match(/(^|;) ?googtrans=([^;]*)(;|$)/); 30 if (cookieMatch && cookieMatch[2]) { 31 var langPair = cookieMatch[2].split('/'); 32 if (langPair.length >= 3) { 33 return langPair[2]; // Returns language code like 'es' or 'zh-CN' 34 } 35 } 36 37 // Fallback to HTML lang attribute 38 var htmlLang = document.documentElement.getAttribute('lang'); 39 if (htmlLang) { 40 return htmlLang.toLowerCase(); 41 } 42 43 return null; 44 } 45 46 /** 47 * Get the language query parameter based on current language 48 * @param {string} lang - Language code 49 * @returns {string|null} Query parameter string or null 50 */ 51 function getLangQueryParam(lang) { 52 if (!lang) { 53 return null; 54 } 55 56 // Normalize language codes 57 lang = lang.toLowerCase(); 58 59 // Spanish 60 if (lang === 'es' || lang === 'es-es' || lang === 'es-mx' || lang.startsWith('es')) { 61 return '?lang=es'; 62 } 63 64 // Chinese Simplified 65 if (lang === 'zh-cn' || lang === 'zh' || lang.startsWith('zh-cn')) { 66 return '?lang=zh-CN'; 67 } 68 69 return null; 70 } 71 72 /** 73 * Check if Google Translate is currently actively translating (not just translated) 74 * This should only return true during the actual translation process 75 */ 76 function isGoogleTranslateActive() { 77 // Only check for actual loading spinner, not other indicators 78 // The skiptranslate element can exist even when translation is done 79 var loadingIndicator = document.querySelector('.goog-te-spinner-pos'); 80 if (loadingIndicator && loadingIndicator.offsetParent !== null) { 81 return true; 82 } 83 84 return false; 85 } 86 87 /** 88 * Update iframe src URLs with language parameter 89 * @param {string} langParam - Language query parameter to append (null to remove) 90 */ 91 function updateIframeUrls(langParam) { 92 // Extract lang value from langParam (e.g., "?lang=es" -> "es") 93 var langValue = langParam ? langParam.replace('?lang=', '') : null; 94 95 // Only update iframes that are not part of Google Translate's own elements 96 var iframes = document.querySelectorAll('iframe[src]'); 97 98 iframes.forEach(function(iframe) { 99 var currentSrc = iframe.getAttribute('src'); 100 if (!currentSrc) { 101 return; 102 } 103 104 // Skip Google Translate's own iframes - be very thorough here 105 if (iframe.closest && ( 106 iframe.closest('.goog-te-banner-frame') || 107 iframe.closest('.goog-te-menu-frame') || 108 iframe.closest('#google_translate_element') || 109 iframe.closest('#google_translate_element2') || 110 iframe.closest('.goog-te-banner') || 111 iframe.closest('.skiptranslate') 112 )) { 113 return; 114 } 115 116 // Skip Google Tag Manager iframes (they shouldn't have lang params) 117 if (currentSrc.indexOf('googletagmanager.com') !== -1) { 118 return; 119 } 120 121 // Skip Google Translate iframes 122 if (currentSrc.indexOf('translate.google.com') !== -1) { 123 return; 124 } 125 126 // Skip iframes that are inside Google Translate containers 127 var parent = iframe.parentElement; 128 while (parent && parent !== document.body) { 129 if (parent.classList && ( 130 parent.classList.contains('goog-te-banner-frame') || 131 parent.classList.contains('goog-te-menu-frame') || 132 parent.classList.contains('goog-te-banner') || 133 parent.id === 'google_translate_element' || 134 parent.id === 'google_translate_element2' 135 )) { 136 return; 137 } 138 parent = parent.parentElement; 139 } 140 141 // Store original src if not already stored (before any modifications) 142 if (!iframe.hasAttribute('data-original-src')) { 143 // Use current src, but remove any existing lang param first 144 var cleanSrc = currentSrc.replace(/[?&]lang=[^&]*/, ''); 145 iframe.setAttribute('data-original-src', cleanSrc); 146 } 147 148 // Get the original src (without lang param) 149 var originalSrc = iframe.getAttribute('data-original-src') || currentSrc.replace(/[?&]lang=[^&]*/, ''); 150 151 // Parse URL to handle existing query strings 152 try { 153 var url = new URL(originalSrc, window.location.href); 154 155 if (langValue) { 156 // Set the lang parameter 157 url.searchParams.set('lang', langValue); 158 } else { 159 // Remove the lang parameter 160 url.searchParams.delete('lang'); 161 } 162 163 var newSrc = url.toString(); 164 165 // Update immediately if the src changed
166 if (iframe.src !== newSrc) { 167 iframe.src = newSrc; 168 } 169 } catch (e) { 170 // Fallback for browsers that don't support URL constructor 171 // or for relative URLs that can't be parsed 172 var cleanSrc = originalSrc.replace(/[?&]lang=[^&]*/, ''); 173 var newSrc; 174 175 if (langValue) { 176 // Add lang parameter 177 var separator = cleanSrc.indexOf('?') !== -1 ? '&' : '?'; 178 newSrc = cleanSrc + separator + 'lang=' + langValue; 179 } else { 180 // Remove lang parameter (already removed in cleanSrc) 181 newSrc = cleanSrc; 182 } 183 184 if (iframe.src !== newSrc) { 185 iframe.src = newSrc; 186 } 187 } 188 }); 189 } 190 191 /** 192 * Main function to check and update iframe URLs 193 */ 194 var lastProcessedLang = null; 195 var updateTimeout = null; 196 var isTranslating = false; 197 198 function checkAndUpdateIframes() { 199 // Clear any pending updates 200 if (updateTimeout) { 201 clearTimeout(updateTimeout); 202 } 203 204 var currentLang = getCurrentLanguage(); 205 var langParam = getLangQueryParam(currentLang); 206 207 // Very short delay to let Google Translate finish its work first 208 // Only delay if Google Translate is actively translating 209 var delay = isGoogleTranslateActive() ? 200 : 50; 210 211 updateTimeout = setTimeout(function() { 212 // Update if language changed 213 if (langParam && currentLang !== lastProcessedLang) { 214 lastProcessedLang = currentLang; 215 updateIframeUrls(langParam); 216 } else if (!langParam && lastProcessedLang) { 217 // Language was reset to default (English), remove lang params 218 lastProcessedLang = null; 219 updateIframeUrls(null); // Pass null to remove lang params 220 } else if (langParam && currentLang === lastProcessedLang) { 221 // Language is the same but might need to update iframes that were added later 222 updateIframeUrls(langParam); 223 } 224 }, delay); 225 } 226 227 /** 228 * Monitor cookie changes (for Google Translate) 229 */ 230 function monitorCookieChanges() { 231 var lastGoogtransCookie = null; 232 var cookieUpdateTimeout = null; 233 234 // Get initial cookie value 235 var cookieMatch = document.cookie.match(/(^|;) ?googtrans=([^;]*)(;|$)/); 236 if (cookieMatch && cookieMatch[2]) { 237 lastGoogtransCookie = cookieMatch[2]; 238 } 239 240 setInterval(function() { 241 var cookieMatch = document.cookie.match(/(^|;) ?googtrans=([^;]*)(;|$)/); 242 var currentGoogtransCookie = cookieMatch && cookieMatch[2] ? cookieMatch[2] : null; 243 244 // Only check if the googtrans cookie specifically changed 245 if (currentGoogtransCookie !== lastGoogtransCookie) { 246 lastGoogtransCookie = currentGoogtransCookie; 247 248 // Clear any pending updates 249 if (cookieUpdateTimeout) { 250 clearTimeout(cookieUpdateTimeout); 251 } 252 253 // Update immediately when cookie changes 254 checkAndUpdateIframes(); 255 } 256 }, 200); // Check every 200ms for faster response 257 258 // Also periodically check for new iframes that might have been added 259 // This helps catch dynamically loaded iframes 260 setInterval(function() { 261 var currentLang = getCurrentLanguage(); 262 var langParam = getLangQueryParam(currentLang); 263 if (langParam) { 264 // Check if there are iframes without lang param 265 var iframes = document.querySelectorAll('iframe[src]'); 266 var needsUpdate = false; 267 for (var i = 0; i < iframes.length; i++) { 268 var iframe = iframes[i]; 269 var src = iframe.getAttribute('src'); 270 if (src && 271 src.indexOf('googletagmanager.com') === -1 && 272 src.indexOf('translate.google.com') === -1 && 273 src.indexOf('lang=') === -1 && 274 !iframe.closest('.goog-te-banner-frame') && 275 !iframe.closest('.goog-te-menu-frame')) { 276 needsUpdate = true; 277 break; 278 } 279 } 280 if (needsUpdate) { 281 checkAndUpdateIframes(); 282 } 283 } 284 }, 1000); // Check every second for new iframes 285 } 286 287 /** 288 * Monitor HTML lang attribute changes only (less intrusive)
289 * REMOVED: This was potentially interfering with Google Translate 290 * We'll rely on cookie monitoring instead which is more reliable 291 */ 292 function monitorLangAttribute() { 293 // Disabled to avoid interference with Google Translate 294 // Cookie monitoring is sufficient and less intrusive 295 } 296 297 /** 298 * Initialize the script 299 */ 300 function init() { 301 // Start immediately 302 if (document.readyState === 'loading') { 303 document.addEventListener('DOMContentLoaded', function() { 304 checkAndUpdateIframes(); 305 // Start monitoring immediately 306 monitorCookieChanges(); 307 monitorLangAttribute(); 308 }); 309 } else { 310 checkAndUpdateIframes(); 311 // Start monitoring immediately 312 monitorCookieChanges(); 313 monitorLangAttribute(); 314 } 315 316 // Also check when Google Translate widget is interacted with 317 // Listen for clicks on language selector links (use bubble phase, not capture) 318 // REMOVED: This was potentially interfering with Google Translate 319 // We'll rely on cookie monitoring instead 320 321 // REMOVED: Direct monitoring of Google Translate select dropdown 322 // This was potentially interfering with Google Translate's functionality 323 // We'll rely on cookie monitoring instead which is less intrusive 324 } 325 326 // Start initialization 327 init(); 328 329})(); 330
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.