|
| 1 | +import { redirect } from 'next/navigation'; |
| 2 | +import { headers } from 'next/headers'; |
| 3 | +import { siteConfig } from '@/lib/site-config'; |
| 4 | + |
| 5 | +/** |
| 6 | + * Root page that redirects to the appropriate language version |
| 7 | + * Detects user's preferred language from Accept-Language header |
| 8 | + */ |
| 9 | +export default async function RootPage() { |
| 10 | + const headersList = await headers(); |
| 11 | + const acceptLanguage = headersList.get('accept-language') || ''; |
| 12 | + |
| 13 | + // Get configured languages and default language from site config |
| 14 | + const availableLanguages = siteConfig.i18n.languages; |
| 15 | + const defaultLanguage = siteConfig.i18n.defaultLanguage; |
| 16 | + |
| 17 | + // Parse Accept-Language header to find the best match |
| 18 | + const preferredLanguage = detectPreferredLanguage(acceptLanguage, availableLanguages); |
| 19 | + |
| 20 | + // Redirect to the detected or default language |
| 21 | + const targetLanguage = preferredLanguage || defaultLanguage; |
| 22 | + redirect(`/${targetLanguage}/docs`); |
| 23 | +} |
| 24 | + |
| 25 | +/** |
| 26 | + * Detect preferred language from Accept-Language header |
| 27 | + * Returns the first available language that matches user preferences |
| 28 | + */ |
| 29 | +function detectPreferredLanguage( |
| 30 | + acceptLanguage: string, |
| 31 | + availableLanguages: string[] |
| 32 | +): string | null { |
| 33 | + if (!acceptLanguage) { |
| 34 | + return null; |
| 35 | + } |
| 36 | + |
| 37 | + // Parse Accept-Language header |
| 38 | + // Example: "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7" |
| 39 | + const languages = acceptLanguage |
| 40 | + .split(',') |
| 41 | + .map(lang => { |
| 42 | + const [code, qValue] = lang.trim().split(';q='); |
| 43 | + const quality = qValue ? parseFloat(qValue) : 1.0; |
| 44 | + // Extract primary language code (e.g., "en" from "en-US") |
| 45 | + const primaryCode = code.split('-')[0].toLowerCase(); |
| 46 | + return { code: primaryCode, quality }; |
| 47 | + }) |
| 48 | + .sort((a, b) => b.quality - a.quality); // Sort by quality (preference) |
| 49 | + |
| 50 | + // Find first matching language that is available |
| 51 | + for (const { code } of languages) { |
| 52 | + // Check if the language code matches any available language |
| 53 | + if (availableLanguages.includes(code)) { |
| 54 | + return code; |
| 55 | + } |
| 56 | + // Special case: map 'zh' to 'cn' |
| 57 | + if (code === 'zh' && availableLanguages.includes('cn')) { |
| 58 | + return 'cn'; |
| 59 | + } |
| 60 | + } |
| 61 | + |
| 62 | + return null; |
| 63 | +} |
0 commit comments