/* ============================================ CPMP Internationalization (i18n) Engine v3 Supports: en, ur, pa, es, fr, de, hi, ja, ar Browser language detection + localStorage ============================================ */ (function () { 'use strict'; var STORAGE_KEY = 'cpmp-lang'; var NOTICE_KEY = 'cpmp-lang-notice'; var DEFAULT_LANG = 'en'; var RTL_LANGS = ['ur', 'pa', 'ar']; var SUPPORTED = ['en', 'ur', 'pa', 'es', 'fr', 'de', 'hi', 'ja', 'ar']; var LANG_LABELS = { en: 'English', ur: 'اردو', pa: 'پنجابی', es: 'Español', fr: 'Français', de: 'Deutsch', hi: 'हिन्दी', ja: '日本語', ar: 'العربية' }; var cache = {}; var applying = false; var browserDetected = false; // true if language was auto-detected from browser // Detect best language: localStorage → browser language → English function detectLanguage() { var stored = localStorage.getItem(STORAGE_KEY); if (stored && SUPPORTED.indexOf(stored) !== -1) return stored; // No stored preference — detect from browser var browserLangs = navigator.languages || [navigator.language || navigator.userLanguage || '']; for (var i = 0; i < browserLangs.length; i++) { var tag = (browserLangs[i] || '').toLowerCase(); // Exact match first (e.g., 'de', 'ja', 'ar') var base = tag.split('-')[0]; if (SUPPORTED.indexOf(base) !== -1) { browserDetected = true; return base; } } return DEFAULT_LANG; } var currentLang = detectLanguage(); // Apply RTL immediately to prevent flash if (RTL_LANGS.indexOf(currentLang) !== -1) { document.documentElement.setAttribute('dir', 'rtl'); document.documentElement.classList.add('rtl'); } document.documentElement.setAttribute('lang', currentLang); function resolve(obj, key) { var parts = key.split('.'); var val = obj; for (var i = 0; i < parts.length; i++) { if (val == null) return undefined; val = val[parts[i]]; } return val; } function applyTranslations(root, translations) { if (!translations || applying) return; applying = true; root = root || document; var els = root.querySelectorAll('[data-i18n]'); for (var i = 0; i < els.length; i++) { var val = resolve(translations, els[i].getAttribute('data-i18n')); if (val !== undefined && els[i].textContent !== val) { els[i].textContent = val; } } var htmlEls = root.querySelectorAll('[data-i18n-html]'); for (var j = 0; j < htmlEls.length; j++) { var hval = resolve(translations, htmlEls[j].getAttribute('data-i18n-html')); if (hval !== undefined) htmlEls[j].innerHTML = hval; } var phEls = root.querySelectorAll('[data-i18n-placeholder]'); for (var k = 0; k < phEls.length; k++) { var pval = resolve(translations, phEls[k].getAttribute('data-i18n-placeholder')); if (pval !== undefined) phEls[k].setAttribute('placeholder', pval); } var altEls = root.querySelectorAll('[data-i18n-alt]'); for (var m = 0; m < altEls.length; m++) { var aval = resolve(translations, altEls[m].getAttribute('data-i18n-alt')); if (aval !== undefined) altEls[m].setAttribute('alt', aval); } applying = false; } function loadLanguage(lang, callback) { if (cache[lang]) { callback(cache[lang]); return; } var scripts = document.getElementsByTagName('script'); var basePath = ''; for (var i = 0; i < scripts.length; i++) { var src = scripts[i].getAttribute('src') || ''; if (src.indexOf('i18n.js') !== -1) { basePath = src.replace(/js\/i18n\.js.*$/, ''); break; } } var xhr = new XMLHttpRequest(); xhr.open('GET', basePath + 'lang/' + lang + '.json', true); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status === 200) { try { cache[lang] = JSON.parse(xhr.responseText); callback(cache[lang]); } catch (e) { console.warn('[i18n] Parse error:', e); callback(null); } } else { callback(null); } } }; xhr.send(); } function updateDropdown(lang) { var flag = document.getElementById('lang-current-flag'); var label = document.getElementById('lang-current-label'); if (flag) flag.src = 'icons/flag-' + lang + '.svg'; if (label) label.textContent = LANG_LABELS[lang] || lang; // Mark active in dropdown list var items = document.querySelectorAll('.lang-option'); for (var i = 0; i < items.length; i++) { if (items[i].getAttribute('data-lang') === lang) { items[i].classList.add('lang-active'); } else { items[i].classList.remove('lang-active'); } } } function showLangNotice(lang, translations, isAutoDetect) { // Auto-detect: only show once (first visit). Manual switch: always show. if (isAutoDetect && localStorage.getItem(NOTICE_KEY)) return; // Don't show for English on auto-detect (it's the default, nothing special happened) if (isAutoDetect && lang === DEFAULT_LANG) return; // Remove any existing notice overlay (e.g., from a previous language switch) var existing = document.getElementById('cpmp-lang-notice-overlay'); if (existing) existing.parentNode.removeChild(existing); var notice = translations.langNotice; if (!notice) return; var langLabel = LANG_LABELS[lang] || lang; var bodyTemplate = isAutoDetect ? (notice.bodyAuto || notice.body || '') : (notice.body || ''); var bodyText = bodyTemplate.replace('{lang}', langLabel); var isRtl = RTL_LANGS.indexOf(lang) !== -1; // Detect Day/Night mode var isLight = document.body.classList.contains('light-mode') || document.documentElement.classList.contains('light-mode'); // Theme-aware colors var modalBg = isLight ? '#ffffff' : '#1a1f2e'; var titleColor = isLight ? '#1a1a2e' : '#e2e8f0'; var bodyColor = isLight ? '#4a5568' : '#cbd5e1'; var privacyBg = isLight ? '#f7fafc' : 'rgba(255,255,255,0.05)'; var privacyColor = isLight ? '#718096' : '#94a3b8'; var separatorColor = isLight ? 'rgba(0,0,0,0.1)' : 'rgba(255,255,255,0.1)'; var enLabelColor = isLight ? '#94a3b8' : '#64748b'; var enTextColor = isLight ? '#64748b' : '#94a3b8'; var overlayBg = isLight ? 'rgba(0,0,0,0.35)' : 'rgba(0,0,0,0.55)'; var modalShadow = isLight ? '0 20px 60px rgba(0,0,0,0.15)' : '0 20px 60px rgba(0,0,0,0.5)'; var modalBorder = isLight ? '1px solid rgba(0,0,0,0.08)' : '1px solid rgba(255,255,255,0.08)'; // Build overlay var overlay = document.createElement('div'); overlay.id = 'cpmp-lang-notice-overlay'; overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:' + overlayBg + ';z-index:99999;display:flex;align-items:center;justify-content:center;padding:1rem;opacity:0;transition:opacity 0.3s ease;'; var modal = document.createElement('div'); modal.style.cssText = 'background:' + modalBg + ';border:' + modalBorder + ';border-radius:16px;max-width:460px;width:100%;padding:2.2rem 2rem;box-shadow:' + modalShadow + ';text-align:center;transform:translateY(20px);transition:transform 0.3s ease;' + (isRtl ? 'direction:rtl;' : ''); var icon = document.createElement('div'); icon.style.cssText = 'font-size:2.4rem;margin-bottom:0.8rem;'; icon.textContent = '🌐'; var title = document.createElement('h3'); title.style.cssText = 'color:' + titleColor + ';font-size:1.25rem;margin:0 0 1rem 0;font-weight:700;'; title.textContent = notice.title || 'Language Preference Saved'; var body = document.createElement('p'); body.style.cssText = 'color:' + bodyColor + ';font-size:0.95rem;line-height:1.7;margin:0 0 1rem 0;text-align:' + (isRtl ? 'right' : 'left') + ';'; body.innerHTML = bodyText; var privacy = document.createElement('p'); privacy.style.cssText = 'color:' + privacyColor + ';font-size:0.85rem;line-height:1.6;margin:0 0 1.5rem 0;text-align:' + (isRtl ? 'right' : 'left') + ';background:' + privacyBg + ';padding:0.8rem 1rem;border-radius:8px;border-left:' + (isRtl ? 'none' : '3px solid #d4a843') + ';border-right:' + (isRtl ? '3px solid #d4a843' : 'none') + ';'; privacy.textContent = notice.privacy || ''; // English translation at the bottom (skip if already English) var enSection = null; if (lang !== DEFAULT_LANG) { enSection = document.createElement('div'); enSection.style.cssText = 'direction:ltr;text-align:left;'; var separator = document.createElement('div'); separator.style.cssText = 'border-top:1px solid ' + separatorColor + ';margin:0 0 1rem 0;padding-top:1rem;'; var enLabel = document.createElement('div'); enLabel.style.cssText = 'color:' + enLabelColor + ';font-size:0.75rem;font-weight:600;text-transform:uppercase;letter-spacing:0.05em;margin-bottom:0.5rem;'; enLabel.textContent = 'English'; // Load English translations for the notice var enNoticeTitle = document.createElement('p'); enNoticeTitle.style.cssText = 'color:' + enTextColor + ';font-size:0.85rem;font-weight:600;margin:0 0 0.4rem 0;'; var enNoticeBody = document.createElement('p'); enNoticeBody.style.cssText = 'color:' + enTextColor + ';font-size:0.82rem;line-height:1.6;margin:0 0 0.4rem 0;'; var enNoticePrivacy = document.createElement('p'); enNoticePrivacy.style.cssText = 'color:' + enTextColor + ';font-size:0.78rem;line-height:1.5;margin:0;opacity:0.8;'; // Use cached English or load it function fillEnglish(enTrans) { if (!enTrans || !enTrans.langNotice) return; var en = enTrans.langNotice; var enBodyTemplate = isAutoDetect ? (en.bodyAuto || en.body || '') : (en.body || ''); enNoticeTitle.textContent = en.title || 'Language Preference Saved'; enNoticeBody.innerHTML = enBodyTemplate.replace('{lang}', langLabel); enNoticePrivacy.textContent = en.privacy || ''; } enSection.appendChild(separator); enSection.appendChild(enLabel); enSection.appendChild(enNoticeTitle); enSection.appendChild(enNoticeBody); enSection.appendChild(enNoticePrivacy); if (cache[DEFAULT_LANG]) { fillEnglish(cache[DEFAULT_LANG]); } else { loadLanguage(DEFAULT_LANG, fillEnglish); } } var btn = document.createElement('button'); btn.style.cssText = 'background:#d4a843;color:#fff;border:none;border-radius:8px;padding:0.75rem 2.5rem;font-size:1rem;font-weight:700;cursor:pointer;transition:background 0.2s,transform 0.2s;font-family:inherit;margin-top:0.5rem;'; btn.textContent = notice.btn || 'Got It'; btn.onmouseover = function () { btn.style.background = '#c49a3a'; btn.style.transform = 'translateY(-1px)'; }; btn.onmouseout = function () { btn.style.background = '#d4a843'; btn.style.transform = 'none'; }; btn.onclick = function () { localStorage.setItem(NOTICE_KEY, '1'); overlay.style.opacity = '0'; modal.style.transform = 'translateY(20px)'; setTimeout(function () { if (overlay.parentNode) overlay.parentNode.removeChild(overlay); }, 300); }; modal.appendChild(icon); modal.appendChild(title); modal.appendChild(body); modal.appendChild(privacy); if (enSection) modal.appendChild(enSection); modal.appendChild(btn); overlay.appendChild(modal); document.body.appendChild(overlay); // Animate in requestAnimationFrame(function () { requestAnimationFrame(function () { overlay.style.opacity = '1'; modal.style.transform = 'translateY(0)'; }); }); } function switchLanguage(lang) { if (SUPPORTED.indexOf(lang) === -1) return; currentLang = lang; localStorage.setItem(STORAGE_KEY, lang); document.documentElement.setAttribute('lang', lang); if (RTL_LANGS.indexOf(lang) !== -1) { document.documentElement.setAttribute('dir', 'rtl'); document.documentElement.classList.add('rtl'); } else { document.documentElement.setAttribute('dir', 'ltr'); document.documentElement.classList.remove('rtl'); } loadLanguage(lang, function (translations) { if (translations) { applyTranslations(document, translations); if (window.cpmpI18n) window.cpmpI18n._translations = translations; showLangNotice(lang, translations, false); } updateDropdown(lang); }); // Close dropdown var menu = document.getElementById('lang-menu'); if (menu) menu.classList.remove('lang-menu-open'); } function init() { // If browser-detected a non-English language, save it and prepare to show notice if (browserDetected) { localStorage.setItem(STORAGE_KEY, currentLang); } // Wait for header/footer to load, then apply translations once var attempts = 0; var maxAttempts = 50; // 50 × 100ms = 5 seconds max function tryApply() { attempts++; var headerLoaded = document.querySelector('.site-header'); var footerLoaded = document.querySelector('.site-footer'); if (headerLoaded && footerLoaded) { // Both loaded — apply translations and bind dropdown loadLanguage(currentLang, function (translations) { if (translations) { applyTranslations(document, translations); if (window.cpmpI18n) window.cpmpI18n._translations = translations; // Show notice on first auto-detected visit if (browserDetected) showLangNotice(currentLang, translations, true); } bindDropdown(); updateDropdown(currentLang); }); return; } if (attempts < maxAttempts) { setTimeout(tryApply, 100); } else { // Timeout — apply what we have loadLanguage(currentLang, function (translations) { if (translations) { applyTranslations(document, translations); if (window.cpmpI18n) window.cpmpI18n._translations = translations; if (browserDetected) showLangNotice(currentLang, translations, true); } bindDropdown(); updateDropdown(currentLang); }); } } // Apply to existing DOM immediately (for elements already in the page) loadLanguage(currentLang, function (translations) { if (translations) { applyTranslations(document, translations); if (window.cpmpI18n) window.cpmpI18n._translations = translations; } }); // Then poll for header/footer tryApply(); } function bindDropdown() { var toggle = document.getElementById('lang-toggle'); var menu = document.getElementById('lang-menu'); if (!toggle || !menu || toggle._bound) return; toggle._bound = true; toggle.addEventListener('click', function (e) { e.stopPropagation(); menu.classList.toggle('lang-menu-open'); }); // Close on outside click document.addEventListener('click', function () { menu.classList.remove('lang-menu-open'); }); menu.addEventListener('click', function (e) { e.stopPropagation(); }); // Bind language options var options = menu.querySelectorAll('.lang-option'); for (var i = 0; i < options.length; i++) { options[i].addEventListener('click', function (e) { e.preventDefault(); switchLanguage(this.getAttribute('data-lang')); }); } } if (document.body) { init(); } else { document.addEventListener('DOMContentLoaded', init); } window.cpmpI18n = { switchLanguage: switchLanguage, getCurrentLang: function () { return currentLang; }, applyTranslations: applyTranslations, _translations: null, _cache: cache }; })();