// SwiftLingo Language Switcher const SwiftLingo = (function () { // Default configuration const defaultConfig = { selector: '#language-switcher', style: 'dropdown', // or 'buttons' position: 'auto', rememberPreference: true, autoDetect: true, showNativeNames: false, targetLanguages: [], originalLanguage: '' }; // Language definitions with native names const languages = { ar: {code: "AR", name: "Arabic", nativeName: "العربية", flag: "🇸🇦"}, bg: {code: "BG", name: "Bulgarian", nativeName: "Bǎlgarski", flag: "🇧🇬"}, cs: {code: "CS", name: "Czech", nativeName: "Čeština", flag: "🇨🇿"}, da: {code: "DA", name: "Danish", nativeName: "Dansk", flag: "🇩🇰"}, de: {code: "DE", name: "German", nativeName: "Deutsch", flag: "🇩🇪"}, el: {code: "EL", name: "Greek", nativeName: "Ελληνικά", flag: "🇬🇷"}, en: {code: "EN", name: "English", nativeName: "English", flag: "🇬🇧"}, es: {code: "ES", name: "Spanish", nativeName: "Español", flag: "🇪🇸"}, et: {code: "ET", name: "Estonian", nativeName: "Eesti", flag: "🇪🇪"}, fi: {code: "FI", name: "Finnish", nativeName: "Suomi", flag: "🇫🇮"}, fr: {code: "FR", name: "French", nativeName: "Français", flag: "🇫🇷"}, hu: {code: "HU", name: "Hungarian", nativeName: "Magyar", flag: "🇭🇺"}, id: {code: "ID", name: "Indonesian", nativeName: "Bahasa Indonesia", flag: "🇮🇩"}, it: {code: "IT", name: "Italian", nativeName: "Italiano", flag: "🇮🇹"}, ja: {code: "JA", name: "Japanese", nativeName: "日本語", flag: "🇯🇵"}, ko: {code: "KO", name: "Korean", nativeName: "한국어", flag: "🇰🇷"}, lt: {code: "LT", name: "Lithuanian", nativeName: "Lietuvių kalba", flag: "🇱🇹"}, lv: {code: "LV", name: "Latvian", nativeName: "Latviešu valoda", flag: "🇱🇻"}, nb: {code: "NB", name: "Norwegian", nativeName: "Norsk", flag: "🇳🇴"}, nl: {code: "NL", name: "Netherlands", nativeName: "Nederlands", flag: "🇳🇱"}, pl: {code: "PL", name: "Polish", nativeName: "Polski", flag: "🇵🇱"}, pt: {code: "PT", name: "Portuguese", nativeName: "Português", flag: "🇧🇷"}, ro: {code: "RO", name: "Romanian", nativeName: "Română", flag: "🇷🇴"}, ru: {code: "RU", name: "Russian", nativeName: "Русский", flag: "🇷🇺"}, sk: {code: "SK", name: "Slovak", nativeName: "Slovenčina", flag: "🇸🇰"}, sl: {code: "SL", name: "Slovenian", nativeName: "Slovenščina", flag: "🇸🇮"}, sv: {code: "SV", name: "Swedish", nativeName: "Svenska", flag: "🇸🇪"}, tr: {code: "TR", name: "Turkish", nativeName: "Türkçe", flag: "🇹🇷"}, uk: {code: "UK", name: "Ukrainian", nativeName: "Українська", flag: "🇺🇦"}, zh: {code: "ZH", name: "Chinese", nativeName: "中文", flag: "🇨🇳"}, } let config = {...defaultConfig}; let currentLanguage = null; // Create styles const createStyles = () => { const style = document.createElement('style'); style.textContent = ` .sl-wrapper { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; font-size: 14px; z-index: 9999; } .sl-wrapper.sl-fixed-tr { position: fixed; top: 20px; right: 20px; } .sl-dropdown { position: relative; display: inline-block; } .sl-dropdown-button { background: white; border: 1px solid #e5e7eb; padding: 8px 12px; border-radius: 6px; cursor: pointer; display: flex; align-items: center; gap: 8px; min-width: 150px; transition: all 0.2s; } .sl-dropdown-button:hover { border-color: #d1d5db; } .sl-dropdown-content { position: absolute; top: 100%; right: 0; margin-top: 4px; background: white; border: 1px solid #e5e7eb; border-radius: 6px; box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1); display: none; min-width: 150px; } .sl-dropdown-content.active { display: block; } .sl-language-option { padding: 8px 12px; cursor: pointer; display: flex; align-items: center; gap: 8px; text-decoration: none; } .sl-language-option:hover { background: #f3f4f6; text-decoration: none; } .sl-buttons { display: flex; gap: 8px; } .sl-button { background: white; border: 1px solid #e5e7eb; padding: 6px 12px; border-radius: 6px; cursor: pointer; display: flex; align-items: center; gap: 4px; transition: all 0.2s; } .sl-button:hover { border-color: #d1d5db; } .sl-button.active { background: #f3f4f6; border-color: #d1d5db; } .sl-flag { font-size: 16px; } `; document.head.appendChild(style); }; // Create dropdown switcher const createDropdown = (container, availableLanguages) => { const dropdown = document.createElement('div'); dropdown.className = 'sl-dropdown'; const button = document.createElement('button'); button.className = 'sl-dropdown-button'; const content = document.createElement('div'); content.className = 'sl-dropdown-content'; // Update button content const updateButtonContent = (lang) => { const selected = languages[lang]; button.innerHTML = ` ${selected.flag} ${config.showNativeNames ? selected.nativeName : selected.name} `; }; const createLanguageOption = (href, lang) => { const option = document.createElement('a'); option.href = href option.className = 'sl-language-option'; option.innerHTML = ` ${lang.flag} ${config.showNativeNames ? lang.nativeName : lang.name} `; option.addEventListener('click', () => { setLanguage(lang); content.classList.remove('active'); updateButtonContent(lang); }); content.appendChild(option); return option }; // Create language options if (config.originalLanguage in languages) { const lang = languages[config.originalLanguage] const option = createLanguageOption(`${window.location.protocol}//${config.hostname}`, lang) content.appendChild(option); } availableLanguages.forEach(lang => { const option = createLanguageOption(`${window.location.protocol}//${lang}.${config.hostname}`, languages[lang]) content.appendChild(option); }); // Toggle dropdown button.addEventListener('click', () => { content.classList.toggle('active'); }); // Close dropdown when clicking outside document.addEventListener('click', (e) => { if (!dropdown.contains(e.target)) { content.classList.remove('active'); } }); updateButtonContent(currentLanguage); dropdown.appendChild(button); dropdown.appendChild(content); container.appendChild(dropdown); }; // Create buttons switcher const createButtons = (container, availableLanguages) => { const buttonGroup = document.createElement('div'); buttonGroup.className = 'sl-buttons'; availableLanguages.forEach(lang => { const button = document.createElement('button'); button.className = `sl-button ${lang === currentLanguage ? 'active' : ''}`; button.innerHTML = ` ${languages[lang].flag} ${config.showNativeNames ? languages[lang].nativeName : languages[lang].code.toUpperCase()} `; button.addEventListener('click', () => { setLanguage(lang); buttonGroup.querySelectorAll('.sl-button').forEach(btn => btn.classList.remove('active')); button.classList.add('active'); }); buttonGroup.appendChild(button); }); container.appendChild(buttonGroup); }; // Detect browser language const detectLanguage = () => { const browserLang = document.documentElement.lang.split('-')[0]; return languages[browserLang] ? browserLang : 'en'; }; // Set language and trigger change const setLanguage = (lang) => { currentLanguage = lang; if (config.rememberPreference) { localStorage.setItem('sl-language', lang); } // Trigger page-specific language change handler window.dispatchEvent(new CustomEvent('languageChange', {detail: {language: lang}})); }; const getAvailableLanguages = (configuredLanguages) => { const result = []; for (let language of configuredLanguages) { if (language in languages) { result.push(language) } } return result } // Initialize the language switcher const init = (userConfig = {}) => { // Merge configurations config = {...defaultConfig, ...userConfig}; // Create styles createStyles(); // Get container const container = document.querySelector(config.selector); if (!container) { console.error('SwiftLingo: Container element not found'); return; } // Setup container container.className = 'sl-wrapper'; if (config.position === 'top-right') { container.classList.add('sl-fixed-tr'); } // Get available languages from the page const availableLanguages = getAvailableLanguages(config.targetLanguages); // Set initial language // currentLanguage = config.rememberPreference && localStorage.getItem('sl-language') // || (config.autoDetect && detectLanguage()) // || 'en'; currentLanguage = detectLanguage() ?? 'en'; // Create switcher based on style if (config.style === 'dropdown') { createDropdown(container, availableLanguages); } else { createButtons(container, availableLanguages); } }; // Public API return { init, setLanguage, getCurrentLanguage: () => currentLanguage }; })();