If the built-in styles do not fit your design, build a completely custom switcher using the helper functions.
php
<?php
// components/language-switcher.php
$languages = lf_get_languages();
$current = lf_get_current_language();
$core = LF_Core::instance();
if (count($languages) <= 1) return;
?>
<div class="my-custom-switcher" data-current="<?php echo esc_attr($current); ?>">
<button class="switcher-toggle" aria-expanded="false" aria-haspopup="true">
<span class="current-flag">
<img src="<?php echo esc_url(plugins_url('langforge/assets/flags/' . $languages[$current]['flag'] . '.svg')); ?>"
alt="" width="20" height="14">
</span>
<span class="current-name"><?php echo esc_html($languages[$current]['native_name']); ?></span>
<svg class="chevron" width="12" height="12" viewBox="0 0 12 12">
<path d="M3 5l3 3 3-3" stroke="currentColor" fill="none" stroke-width="1.5"/>
</svg>
</button>
<ul class="switcher-menu" role="menu" hidden>
<?php foreach ($languages as $code => $lang) : ?>
<?php if ($code === $current) continue; ?>
<li role="menuitem">
<a href="<?php echo esc_url($core->get_language_url($code)); ?>">
<img src="<?php echo esc_url(plugins_url('langforge/assets/flags/' . $lang['flag'] . '.svg')); ?>"
alt="" width="20" height="14">
<span><?php echo esc_html($lang['native_name']); ?></span>
</a>
</li>
<?php endforeach; ?>
</ul>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const switcher = document.querySelector('.my-custom-switcher');
const toggle = switcher.querySelector('.switcher-toggle');
const menu = switcher.querySelector('.switcher-menu');
toggle.addEventListener('click', function() {
const expanded = toggle.getAttribute('aria-expanded') === 'true';
toggle.setAttribute('aria-expanded', !expanded);
menu.hidden = expanded;
});
document.addEventListener('click', function(e) {
if (!switcher.contains(e.target)) {
toggle.setAttribute('aria-expanded', 'false');
menu.hidden = true;
}
});
});
</script>css
.lf-language-switcher.lf-flags {
display: flex;
gap: 8px;
align-items: center;
}
.lf-language-switcher.lf-flags .wpml-ls-item {
list-style: none;
}
.lf-language-switcher.lf-flags .wpml-ls-link {
display: block;
opacity: 0.6;
transition: opacity 0.2s, transform 0.2s;
}
.lf-language-switcher.lf-flags .wpml-ls-link:hover {
opacity: 1;
transform: scale(1.15);
}
.lf-language-switcher.lf-flags .wpml-ls-current-language .wpml-ls-link {
opacity: 1;
}
.lf-language-switcher.lf-flags .wpml-ls-flag {
width: 28px;
height: 20px;
border-radius: 3px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
}php
<?php
// A switcher that uses JavaScript to switch language without full page reload
// when the translation exists. Falls back to standard link if not.
?>
<div class="ajax-lang-switcher">
<?php
$languages = lf_get_languages();
$current = lf_get_current_language();
$core = LF_Core::instance();
foreach ($languages as $code => $lang) :
$active = ($code === $current) ? ' active' : '';
$url = esc_url($core->get_language_url($code));
?>
<a href="<?php echo $url; ?>"
class="lang-btn<?php echo $active; ?>"
data-lang="<?php echo esc_attr($code); ?>">
<?php echo esc_html($lang['native_name']); ?>
</a>
<?php endforeach; ?>
</div>
<script>
document.querySelectorAll('.ajax-lang-switcher .lang-btn').forEach(function(btn) {
btn.addEventListener('click', function(e) {
// Store scroll position before navigation
sessionStorage.setItem('lf_scroll_y', window.scrollY);
});
});
// Restore scroll position after page load
document.addEventListener('DOMContentLoaded', function() {
var scrollY = sessionStorage.getItem('lf_scroll_y');
if (scrollY) {
window.scrollTo(0, parseInt(scrollY, 10));
sessionStorage.removeItem('lf_scroll_y');
}
});
</script>—