From 00ffe80e13868c98809b519e88e7e7aa27e2fe69 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 16 Jan 2026 10:04:13 +0000 Subject: [PATCH 1/3] Initial plan From 77697eb7cd87ddc07dfe1c3aa38b9e4bd101db4c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 16 Jan 2026 10:11:06 +0000 Subject: [PATCH 2/3] Implement automatic language detection and redirection Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com> --- app/page.tsx | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 839988d..903f940 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,5 +1,46 @@ import { redirect } from 'next/navigation'; +import { headers } from 'next/headers'; +import { i18n } from '@/lib/i18n'; -export default function HomePage() { - redirect('/en/docs'); +export default async function HomePage() { + const headersList = await headers(); + const acceptLanguage = headersList.get('accept-language') || ''; + + // Parse the Accept-Language header and find the best match + const preferredLang = getPreferredLanguage(acceptLanguage, i18n.languages); + + redirect(`/${preferredLang}/docs`); +} + +function getPreferredLanguage(acceptLanguage: string, availableLanguages: readonly string[]): string { + // Parse Accept-Language header (e.g., "zh-CN,zh;q=0.9,en;q=0.8") + const languages = acceptLanguage + .split(',') + .map(lang => { + const parts = lang.trim().split(';'); + const code = parts[0]; + const quality = parts[1] ? parseFloat(parts[1].split('=')[1]) : 1.0; + return { code, quality }; + }) + .sort((a, b) => b.quality - a.quality); + + // Try to find the best match (exact or partial) respecting quality order + for (const lang of languages) { + // Try exact match first + if (availableLanguages.includes(lang.code)) { + return lang.code; + } + + // Try partial match (e.g., "zh" matches "zh-CN") + const langPrefix = lang.code.split('-')[0].toLowerCase(); + const match = availableLanguages.find(available => + available.toLowerCase().startsWith(langPrefix) + ); + if (match) { + return match; + } + } + + // Default to the configured default language + return i18n.defaultLanguage; } From 29b039c30529c5cbebfaedf6d0d9634605f8724f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 16 Jan 2026 10:16:38 +0000 Subject: [PATCH 3/3] Add robust error handling for Accept-Language header parsing Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com> --- app/page.tsx | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 903f940..744c722 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -18,10 +18,28 @@ function getPreferredLanguage(acceptLanguage: string, availableLanguages: readon .split(',') .map(lang => { const parts = lang.trim().split(';'); - const code = parts[0]; - const quality = parts[1] ? parseFloat(parts[1].split('=')[1]) : 1.0; + const code = parts[0].trim(); + + // Skip empty language codes + if (!code) { + return null; + } + + // Parse quality value, default to 1.0 if not present or invalid + let quality = 1.0; + if (parts[1]) { + const qPart = parts[1].trim(); + if (qPart.startsWith('q=')) { + const parsedQuality = parseFloat(qPart.substring(2)); + if (!isNaN(parsedQuality)) { + quality = parsedQuality; + } + } + } + return { code, quality }; }) + .filter((lang): lang is { code: string; quality: number } => lang !== null) .sort((a, b) => b.quality - a.quality); // Try to find the best match (exact or partial) respecting quality order