Fix détection langue Google pour les pages arabes : hreflang + localisation UI

- decodeURIComponent dans getAlternateUrls pour matcher les pathnames arabes percent-encoded
- Localisation des aria-label et title (logo, menu mobile, dark mode toggle) via i18n.ts
- Ajout section ui dans les traductions (closeMenu, changeTheme, themeAuto/Light/Dark)
- Refacto logo.astro : utilise getHomePath/getLocaleFromUrl au lieu de logique dupliquée
- Refacto header.astro : utilise getLocaleFromUrl au lieu de détection manuelle
This commit is contained in:
Jalil Arfaoui 2026-02-26 12:06:55 +01:00
parent 6df73851e5
commit d53039c75d
5 changed files with 27 additions and 14 deletions

View file

@ -1,12 +1,19 @@
--- ---
// DarkModeToggle component - cycles between auto / light / dark import { getLocaleFromUrl, t } from "../utils/i18n";
const currentLang = getLocaleFromUrl(Astro.url);
--- ---
<button <button
id="darkModeToggle" id="darkModeToggle"
type="button" type="button"
class="flex items-center justify-center w-8 h-8 rounded-lg cursor-pointer text-neutral-600 dark:text-neutral-300 hover:text-neutral-900 dark:hover:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 transition-colors" class="flex items-center justify-center w-8 h-8 rounded-lg cursor-pointer text-neutral-600 dark:text-neutral-300 hover:text-neutral-900 dark:hover:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 transition-colors"
aria-label="Changer le thème" aria-label={t('ui', 'changeTheme', currentLang)}
data-theme-labels={JSON.stringify({
auto: t('ui', 'themeAuto', currentLang),
light: t('ui', 'themeLight', currentLang),
dark: t('ui', 'themeDark', currentLang),
})}
> >
<!-- Auto icon (system preference) --> <!-- Auto icon (system preference) -->
<svg <svg
@ -81,8 +88,8 @@
function updateAriaLabel(preference: 'auto' | 'light' | 'dark') { function updateAriaLabel(preference: 'auto' | 'light' | 'dark') {
const toggle = document.getElementById('darkModeToggle'); const toggle = document.getElementById('darkModeToggle');
const labels = { auto: 'Thème : automatique', light: 'Thème : clair', dark: 'Thème : sombre' }; const labels = JSON.parse(toggle?.dataset.themeLabels || '{}');
toggle?.setAttribute('aria-label', labels[preference]); toggle?.setAttribute('aria-label', labels[preference] || preference);
} }
const cycle: Record<string, 'light' | 'dark' | 'auto'> = { auto: 'light', light: 'dark', dark: 'auto' }; const cycle: Record<string, 'light' | 'dark' | 'auto'> = { auto: 'light', light: 'dark', dark: 'auto' };

View file

@ -1,9 +1,8 @@
--- ---
import Logo from "../components/logo.astro"; import Logo from "../components/logo.astro";
import { getLocaleFromUrl, t } from "../utils/i18n";
// Détection de la langue courante par le path uniquement const currentLang = getLocaleFromUrl(Astro.url);
const pathname = Astro.url.pathname;
const currentLang = pathname.startsWith('/en') ? 'en' : pathname.startsWith('/ar') ? 'ar' : 'fr';
// Menu localisé // Menu localisé
const menus = { const menus = {
@ -40,7 +39,7 @@ const currentMenus = menus[currentLang] || menus.fr;
<button <button
id="mobileMenuBackground" id="mobileMenuBackground"
type="button" type="button"
aria-label="Fermer le menu" aria-label={t('ui', 'closeMenu', currentLang)}
class="fixed inset-0 z-20 hidden w-screen h-screen duration-300 ease-out bg-white/90 dark:bg-neutral-950/90 appearance-none border-0 p-0 cursor-default" class="fixed inset-0 z-20 hidden w-screen h-screen duration-300 ease-out bg-white/90 dark:bg-neutral-950/90 appearance-none border-0 p-0 cursor-default"
> >
</button> </button>

View file

@ -1,17 +1,17 @@
--- ---
import HomeIcon from "./icons/HomeIcon.astro"; import HomeIcon from "./icons/HomeIcon.astro";
import { getLocaleFromUrl, getHomePath, t } from "../utils/i18n";
const pathname = Astro.url.pathname; const currentLang = getLocaleFromUrl(Astro.url);
const currentLang = pathname.startsWith('/en') ? 'en' : pathname.startsWith('/ar') ? 'ar' : 'fr'; const homeUrl = getHomePath(currentLang);
const isHome = pathname === '/' || pathname === '/en' || pathname === '/en/' || pathname === '/ar' || pathname === '/ar/'; const isHome = (Astro.url.pathname.replace(/\/$/, '') || '/') === homeUrl;
const homeUrl = currentLang === 'en' ? '/en' : currentLang === 'ar' ? '/ar' : '/';
--- ---
{!isHome && ( {!isHome && (
<a <a
href={homeUrl} href={homeUrl}
class="group relative z-30 flex items-center gap-3 text-neutral-600 dark:text-neutral-300 hover:text-black dark:hover:text-white transition-colors" class="group relative z-30 flex items-center gap-3 text-neutral-600 dark:text-neutral-300 hover:text-black dark:hover:text-white transition-colors"
title="Accueil" title={t('nav', 'home', currentLang)}
> >
<HomeIcon size={20} class="group-hover:scale-110 transition-transform duration-200" /> <HomeIcon size={20} class="group-hover:scale-110 transition-transform duration-200" />
<span class="font-medium text-lg tracking-wide whitespace-nowrap">Jalil Arfaoui</span> <span class="font-medium text-lg tracking-wide whitespace-nowrap">Jalil Arfaoui</span>

View file

@ -63,6 +63,13 @@ export const translations = {
darkMode: { fr: 'Mode sombre', en: 'Dark mode', ar: 'الوضع الداكن' }, darkMode: { fr: 'Mode sombre', en: 'Dark mode', ar: 'الوضع الداكن' },
lightMode: { fr: 'Mode clair', en: 'Light mode', ar: 'الوضع الفاتح' }, lightMode: { fr: 'Mode clair', en: 'Light mode', ar: 'الوضع الفاتح' },
}, },
ui: {
closeMenu: { fr: 'Fermer le menu', en: 'Close menu', ar: 'إغلاق القائمة' },
changeTheme: { fr: 'Changer le thème', en: 'Change theme', ar: 'تغيير المظهر' },
themeAuto: { fr: 'Thème : automatique', en: 'Theme: auto', ar: 'المظهر: تلقائي' },
themeLight: { fr: 'Thème : clair', en: 'Theme: light', ar: 'المظهر: فاتح' },
themeDark: { fr: 'Thème : sombre', en: 'Theme: dark', ar: 'المظهر: داكن' },
},
categories: { categories: {
pro: { fr: 'Professionnel', en: 'Professional', ar: 'مهني' }, pro: { fr: 'Professionnel', en: 'Professional', ar: 'مهني' },
comedy: { fr: 'Comédie', en: 'Comedy', ar: 'كوميديا' }, comedy: { fr: 'Comédie', en: 'Comedy', ar: 'كوميديا' },

View file

@ -79,6 +79,6 @@ function matchDynamicPattern(
export function getAlternateUrls( export function getAlternateUrls(
pathname: string, pathname: string,
): Record<Locale, string> | undefined { ): Record<Locale, string> | undefined {
const normalized = pathname.replace(/\/$/, "") || "/"; const normalized = decodeURIComponent(pathname.replace(/\/$/, "") || "/");
return pathIndex.get(normalized) ?? matchDynamicPattern(normalized); return pathIndex.get(normalized) ?? matchDynamicPattern(normalized);
} }