MediaWiki:Gadget-I18nSwitcher.js

From Bodhicitta
Revision as of 19:18, 2 October 2026 by Jeremi (talk | contribs) (Language switcher: segmented pill (as on PLL bilingual pages), aria-pressed)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
// Universal language switcher for logged-in and anonymous users
(function() {
    'use strict';

    var currentLang = 'en';
    var storageKey = 'bca-language-pref';
    var isLoggedIn = false;
    
    // Check if user is logged in
    function checkLoginStatus() {
        if (window.mw && mw.user) {
            isLoggedIn = !mw.user.isAnon();
        } else {
            // Fallback: check for login-specific elements
            isLoggedIn = document.getElementById('pt-userpage') !== null;
        }
        return isLoggedIn;
    }
    
    // Get stored language preference
    function getStoredLang() {
        try {
            // For logged-in users, try MediaWiki user options first
            if (isLoggedIn && window.mw && mw.user && mw.user.options) {
                var userPref = mw.user.options.get('bca-language');
                if (userPref) {
                    return userPref;
                }
            }
            
            // For all users, try localStorage
            if (window.localStorage) {
                var stored = localStorage.getItem(storageKey);
                if (stored) {
                    return stored;
                }
            }
            
            // Fallback to cookie
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = cookies[i].trim();
                if (cookie.indexOf(storageKey + '=') === 0) {
                    return cookie.substring(storageKey.length + 1);
                }
            }
        } catch (e) {
            console.log('Error getting stored language:', e);
        }
        return 'en';
    }
    
    // Store language preference
    function storeLang(lang) {
        try {
            // For logged-in users, save to MediaWiki preferences
            if (isLoggedIn && window.mw && mw.user && mw.user.options && mw.Api) {
                mw.user.options.set('bca-language', lang);
                new mw.Api().saveOption('bca-language', lang).catch(function(error) {
                    console.log('Failed to save to user preferences:', error);
                });
            }
            
            // For all users, save to localStorage
            if (window.localStorage) {
                localStorage.setItem(storageKey, lang);
            }
            
            // Fallback to cookie for all users
            var expires = new Date();
            expires.setTime(expires.getTime() + (365 * 24 * 60 * 60 * 1000)); // 1 year
            document.cookie = storageKey + '=' + lang + '; expires=' + expires.toUTCString() + '; path=/';
            
        } catch (e) {
            console.log('Error storing language:', e);
        }
    }
    
    function createLanguageToggle() {
        var toggle = document.createElement('div');
        toggle.className = 'lang-toggle nowrap';
        
        // Add user type indicator for debugging (remove in production)
        var userType = isLoggedIn ? ' (logged in)' : ' (anonymous)';
        
        // A segmented pill (as on PLL's bilingual pages): one track, the active
        // language filled. Labels stay in their own script.
        toggle.setAttribute('role', 'group');
        toggle.setAttribute('aria-label', 'Language');
        toggle.innerHTML = '<button type="button" data-lang="en" class="lang-btn" aria-pressed="false">English</button><button type="button" data-lang="bo" class="lang-btn lang-btn--bo" lang="bo" aria-pressed="false">བོད་སྐད་</button>';
        
        // Try different locations for the toggle
        var targetElement = document.querySelector('.navbar-collapse > .navbar-nav:first-of-type');
        
        if (targetElement) {
            targetElement.appendChild(toggle);
        }
        
        // Add click handlers
        toggle.onclick = function(e) {
            var target = e.target || e.srcElement;
            if (target.className.indexOf('lang-btn') !== -1) {
                var lang = target.getAttribute('data-lang');
                if (lang) {
                    switchLanguage(lang);
                }
            }
        };
        
        // Initialize with stored preference
        currentLang = getStoredLang();
        updateActiveButton();
        switchLanguage(currentLang);
    }
    
    function updateActiveButton() {
        var buttons = document.querySelectorAll('.lang-btn') || 
                     document.getElementsByClassName('lang-btn');
        
        for (var i = 0; i < buttons.length; i++) {
            var btn = buttons[i];
            if (btn.getAttribute('data-lang') === currentLang) {
                btn.className = btn.className.replace(' active', '') + ' active';
                btn.setAttribute('aria-pressed', 'true');
            } else {
                btn.className = btn.className.replace(' active', '');
                btn.setAttribute('aria-pressed', 'false');
            }
        }
    }
    
    function switchLanguage(lang) {
        currentLang = lang;
        storeLang(lang);
        updateActiveButton();

        // Switch all i18n elements
        var elements = document.querySelectorAll('.i18n-text') ||
                      document.getElementsByClassName('i18n-text');

        for (var i = 0; i < elements.length; i++) {
            var el = elements[i];
            var targetSpan = el.querySelector('.i18n-lang-' + lang);
            var defaultLang = el.getAttribute('data-default') || 'en';
            var fallbackSpan = el.querySelector('.i18n-lang-' + defaultLang);

            // Hide all lang spans, then show the right one
            var langSpans = el.querySelectorAll('[class^="i18n-lang-"]');
            for (var j = 0; j < langSpans.length; j++) {
                langSpans[j].style.display = 'none';
            }
            if (targetSpan) {
                // Use block display for div elements, inline for span
                targetSpan.style.display = targetSpan.tagName === 'DIV' ? 'block' : 'inline';
            } else if (fallbackSpan) {
                fallbackSpan.style.display = fallbackSpan.tagName === 'DIV' ? 'block' : 'inline';
            }

            if (currentLang === 'bo') {
                el.classList.add('text-130', 'line15');
            } else {
                el.classList.remove('text-130', 'line15');
            }
        }
    }
    
    // Initialize when DOM is ready
    function init() {
        checkLoginStatus();

        // Re-run switchLanguage on any new .i18n-text nodes injected by SMW deferred queries
        function watchForI18nContent() {
            if (!window.MutationObserver) return;
            var observer = new MutationObserver(function(mutations) {
                var found = false;
                for (var m = 0; m < mutations.length; m++) {
                    var nodes = mutations[m].addedNodes;
                    for (var n = 0; n < nodes.length; n++) {
                        var node = nodes[n];
                        if (node.nodeType !== 1) continue;
                        if (node.classList && node.classList.contains('i18n-text') ||
                            node.querySelector && node.querySelector('.i18n-text')) {
                            found = true;
                            break;
                        }
                    }
                    if (found) break;
                }
                if (found) {
                    if (!document.querySelector('.lang-toggle')) {
                        createLanguageToggle();
                    } else {
                        switchLanguage(currentLang);
                    }
                }
            });
            observer.observe(document.body, { childList: true, subtree: true });
        }

        function setup() {
            if (document.querySelector('.i18n-text')) {
                createLanguageToggle();
            }
            // Always watch: SMW deferred queries may inject .i18n-text after init
            watchForI18nContent();
        }

        // Prefer mw.hook so we re-run after cached/ajax content loads
        if (window.mw && mw.hook) {
            mw.hook('wikipage.content').add(function() {
                // Remove any previously inserted toggle to avoid duplicates on re-fire
                var existing = document.querySelector('.lang-toggle');
                if (existing) {
                    existing.parentNode.removeChild(existing);
                }
                setup();
            });
        } else if (document.readyState === 'complete' || document.readyState === 'interactive') {
            setup();
        } else {
            document.addEventListener('DOMContentLoaded', setup);
        }
    }

    init();
})();