Hovering on LocaleSwitcher menu changes locale
Closed this issue · 3 comments
I've got a bit of a weird problem. I'm using svelte headlessui + tailwindui + typesafe i18n and I implemented a LocaleSwitcher.svelte like:
<script lang="ts">
import type { Locales } from '$lib/i18n/i18n-types';
import { setLocale, locale } from '$lib/i18n/i18n-svelte';
import { loadLocaleAsync } from '$lib/i18n/i18n-util.async';
import { replaceLocaleInUrl } from '$lib/utils';
import { page } from '$app/stores';
import { invalidateAll } from '$app/navigation';
import { browser } from '$app/environment';
import { Menu, MenuButton, Transition, MenuItems, MenuItem } from '@rgossiaux/svelte-headlessui';
import {
ChevronDownIcon,
DocumentDuplicateIcon,
GlobeIcon
} from '@rgossiaux/svelte-heroicons/outline';
import { baseLocale, locales } from '$lib/i18n/i18n-util';
import { localeIcons } from '$lib/utils/locale-utils/locale-utils';
// This lang value is used to implement the empty route = baseLocale logic
// if $page.params.lang is null or undefined, it should be baseLocale
$: paramLang = ($page.params.lang as Locales) ?? baseLocale;
const switchLocale = async (newLocale: Locales, updateHistoryState = true) => {
debugger;
if (!newLocale || $locale === newLocale) return;
// load new dictionary from server
await loadLocaleAsync(newLocale);
// select locale
setLocale(newLocale);
// update `lang` attribute
document.querySelector('html')?.setAttribute('lang', newLocale);
if (updateHistoryState) {
// update url to reflect locale changes
history.pushState({ locale: newLocale }, '', replaceLocaleInUrl($page.url, newLocale));
}
// run the `load` function again
invalidateAll();
};
// update locale when navigating via browser back/forward buttons
const handlePopStateEvent = async ({ state }: PopStateEvent) => switchLocale(state.locale, false);
// update locale when page store changes
$: if (browser) {
const lang = paramLang;
switchLocale(lang, false);
history.replaceState(
{ ...history.state, locale: lang },
'',
replaceLocaleInUrl($page.url, lang)
);
}
</script>
<!-- This line handles back and next browser actions -->
<svelte:window on:popstate={handlePopStateEvent} />
<Menu as="div" class="relative inline-block text-left">
<div>
<MenuButton class="btn-outline btn-secondary btn">
<GlobeIcon class="h-6 w-6" />
<ChevronDownIcon class="-mr-1 ml-2 h-5 w-5" aria-hidden="true" />
</MenuButton>
</div>
<Transition
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<MenuItems
class="absolute right-0 z-10 mt-2 w-56 origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
>
<div class="py-1">
{#each locales as l}
<MenuItem let:active>
<a
href={`${replaceLocaleInUrl($page.url, l)}`}
class={`${
active ? 'bg-gray-100 text-gray-900' : 'text-gray-700'
} group flex items-center px-4 py-2 text-sm`}
>
<img class="mr-3 h-5 w-5" src={localeIcons[l]} alt="Icon for {l}" />
{l}
</a>
</MenuItem>
{/each}
</div>
</MenuItems>
</Transition>
</Menu>
The problem is when I'm hovering over the <a>
tags, it's switching the locale of the page. I just have no clue what might be the cause of this so I'm sorry if this is not the appropriate place. Any tips on how I might find what's happening here?
Adding data-sveltekit-preload-data='tap'
to anchor tags fixed the issue.
I stumbled upon the same issue. This happens because preloaded page overrides current locale in the src/routes/+layout.ts
as it's being re-run in browser.
I think I found an alternate way to resolve this. We just check if locale store is already set in browser and if yes, make so it overrides detected locale.
// src/routes/+layout.ts
import { locale as svelteLocale } from '$i18n/i18n-svelte.js';
import { browser } from '$app/environment';
export const load: LayoutLoad = async ({ data: { locale } }) => {
// place at the top before everything else
// prevent preloaded page locale from overriding current locale in browser
if (browser) {
const $svelteLocale = get(svelteLocale);
if ($svelteLocale !== undefined) {
locale = $svelteLocale;
}
}
// resume normal behaviour
// load dictionary into memory
await loadLocaleAsync(locale);
setLocale(locale);
}