This commit is contained in:
Your Name committed 2026-06-20 18:39:03 -04:00
1 parent 3c4bb06003
commit f7aa4d099f
36 files changed
+528 -180

No files matched your search

+27 -29
View File
@@ -1,34 +1,32 @@
import { useState, useEffect } from 'react'
import i18n from '../lib/i18n'
import { useParams, usePathname } from 'next/navigation';
import { useMemo } from 'react';
import { SUPPORTED_LOCALES, DEFAULT_LOCALE } from '../lib/locales';
/**
* Returns a hydration-safe 2-char language code.
*
* WHY this pattern is necessary:
* - i18n.ts reads localStorage synchronously at module-load time (before React hydrates)
* - So i18n.language may already be 'ja', 'ko', etc. when client components first render
* - useTranslation() subscribes to this and causes components to render the user's language
* DURING hydration, but the server rendered 'th' — causing a mismatch error
*
* SOLUTION:
* - Always return 'th' until the component has mounted (matching the server)
* - After mount, sync to the real language and subscribe to future changes
* - Components using this hook NEVER cause a hydration mismatch
* Returns the current active language based on the URL.
* With Locale Routing, the language is dictated by the first path segment
* (e.g., /en/shop -> 'en').
*/
export function useLang(): string {
// Always 'th' on first render (matches SSR) — updated post-hydration
const [lang, setLang] = useState('th')
useEffect(() => {
// Sync immediately after hydration
const current = i18n.language ? i18n.language.substring(0, 2) : 'th'
setLang(current)
// Subscribe to future language changes (user clicks language button)
const handler = (lng: string) => setLang(lng.substring(0, 2))
i18n.on('languageChanged', handler)
return () => { i18n.off('languageChanged', handler) }
}, [])
return lang
const params = useParams();
const pathname = usePathname();
return useMemo(() => {
// 1. Try to get it from route params
if (params?.lang && typeof params.lang === 'string') {
if (SUPPORTED_LOCALES.includes(params.lang as any)) {
return params.lang;
}
}
// 2. Fallback: extract from pathname if params are not available yet
if (pathname) {
const segments = pathname.split('/').filter(Boolean);
if (segments.length > 0 && SUPPORTED_LOCALES.includes(segments[0] as any)) {
return segments[0];
}
}
return DEFAULT_LOCALE;
}, [params, pathname]);
}