feat(i18n): Integrate next-intl, localize core UI, add regional locales and zh-TW Discover sources

**Overview**
- Integrates next-intl (App Router, no i18n routing) with cookie-based locale and Accept-Language fallback.
- Adds message bundles and regional variants; sets en-US as the default.

**Key changes**
- i18n foundation
  - Adds request-scoped config to load messages per locale and injects NextIntlClientProvider in [layout.tsx]
  - Adds/updates messages for: en-US, en-GB, zh-TW, zh-HK, zh-CN, ja, ko, fr-FR, fr-CA, de.
Centralizes LOCALES, LOCALE_LABELS, and DEFAULT_LOCALE in [locales.ts]
  - Adds LocaleSwitcher (cookie-based) and [LocaleBootstrap]

- Pages and components
  - Localizes Sidebar, Home (including metadata/manifest), Settings, Discover, Library.
  - Localizes common components: MessageInput, Attach, Focus, Optimization, MessageBox, MessageSources, SearchImages, SearchVideos, EmptyChat, NewsArticleWidget, WeatherWidget.

- APIs
  - Weather API returns localized condition strings server-side.

- UX and quality
  - Converts all remaining <img> to Next Image.
  - Updates browserslist/caniuse DB to silence warnings.
  - Security: Settings API Key inputs are now password fields and placeholders were removed.
This commit is contained in:
wei840222 2025-08-16 12:27:18 +08:00
parent 0dc17286b9
commit 9a772d6abe
56 changed files with 3673 additions and 365 deletions

View file

@ -6,6 +6,7 @@ import Link from 'next/link';
import { useSelectedLayoutSegments } from 'next/navigation';
import React, { useState, type ReactNode } from 'react';
import Layout from './Layout';
import { useTranslations } from 'next-intl';
const VerticalIconContainer = ({ children }: { children: ReactNode }) => {
return (
@ -15,25 +16,26 @@ const VerticalIconContainer = ({ children }: { children: ReactNode }) => {
const Sidebar = ({ children }: { children: React.ReactNode }) => {
const segments = useSelectedLayoutSegments();
const t = useTranslations('navigation');
const navLinks = [
{
icon: Home,
href: '/',
active: segments.length === 0 || segments.includes('c'),
label: 'Home',
label: t('home'),
},
{
icon: Search,
href: '/discover',
active: segments.includes('discover'),
label: 'Discover',
label: t('discover'),
},
{
icon: BookOpenText,
href: '/library',
active: segments.includes('library'),
label: 'Library',
label: t('library'),
},
];