// 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
};
})();