feat(i18n): Add next-intl framework with full bilingual support (EN/ZH)

- Implement next-intl framework for scalable i18n
- Add complete Chinese (Simplified) localization
- Support 400+ translated strings across all pages
- Add language switcher with persistent preference
- Zero breaking changes to existing functionality

Framework additions:
- i18n routing and middleware
- LocaleContext for client-side state
- LanguageSwitcher component
- Translation files (en.json, zh.json)

Translated components:
- Homepage: Hero, features, CTA, navbar
- Auth: Login, register
- Dashboard: Main page, layout
- Connectors: Management, add page (all categories)
- Documents: Upload, manage, filters
- Settings: LLM configs, role assignments
- Onboarding: Add provider, assign roles
- Logs: Task logs viewer

Adding a new language now requires only:
1. Create messages/<locale>.json
2. Add locale to i18n/routing.ts
This commit is contained in:
Differ 2025-10-26 14:05:46 +08:00
parent 8aeaf419d0
commit f58c7e4602
37 changed files with 2267 additions and 542 deletions

View file

@ -5,6 +5,7 @@ import { AnimatePresence, motion } from "motion/react";
import { useSearchParams } from "next/navigation";
import { Suspense, useEffect, useState } from "react";
import { toast } from "sonner";
import { useTranslations } from "next-intl";
import { Logo } from "@/components/Logo";
import { getAuthErrorDetails, shouldRetry } from "@/lib/auth-errors";
import { AmbientBackground } from "./AmbientBackground";
@ -12,6 +13,8 @@ import { GoogleLoginButton } from "./GoogleLoginButton";
import { LocalLoginForm } from "./LocalLoginForm";
function LoginContent() {
const t = useTranslations('auth');
const tCommon = useTranslations('common');
const [authType, setAuthType] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [urlError, setUrlError] = useState<{ title: string; message: string } | null>(null);
@ -26,15 +29,15 @@ function LoginContent() {
// Show registration success message
if (registered === "true") {
toast.success("Registration successful!", {
description: "You can now sign in with your credentials",
toast.success(t('register_success'), {
description: t('login_subtitle'),
duration: 5000,
});
}
// Show logout confirmation
if (logout === "true") {
toast.success("Logged out successfully", {
toast.success(tCommon('success'), {
description: "You have been securely logged out",
duration: 3000,
});
@ -93,7 +96,7 @@ function LoginContent() {
<Logo className="rounded-md" />
<div className="mt-8 flex items-center space-x-2">
<Loader2 className="h-5 w-5 animate-spin text-muted-foreground" />
<span className="text-muted-foreground">Loading...</span>
<span className="text-muted-foreground">{tCommon('loading')}</span>
</div>
</div>
</div>
@ -110,7 +113,7 @@ function LoginContent() {
<div className="mx-auto flex h-screen max-w-lg flex-col items-center justify-center">
<Logo className="rounded-md" />
<h1 className="my-8 text-xl font-bold text-neutral-800 dark:text-neutral-100 md:text-4xl">
Sign In
{t('sign_in')}
</h1>
{/* URL Error Display */}