feat(web): integrate localized application catalogs

- compose app, UI, and independently imported block catalogs from JSON configuration\n- persist the selected locale on the server and activate it without hydration flicker\n- add a flag-based language switcher and localized navigation descriptors\n- mount the redesigned i18n devtool only in development\n- regenerate English and Simplified Chinese catalogs and add switcher coverage
This commit is contained in:
Maofeng
2026-07-30 14:48:25 +08:00
parent 6af4d7b798
commit 8cf3a1f03a
19 changed files with 2474 additions and 135 deletions
+16 -5
View File
@@ -24,10 +24,19 @@ import { HotkeysProvider } from "@tanstack/react-hotkeys"
import { TooltipProvider } from "@workspace/ui/components/tooltip"
import { QueryClientProvider } from "@tanstack/react-query"
import { AppI18nProvider } from "@/i18n/app-i18n-provider"
import { getAppLocale } from "@/server/app-locale.functions"
import type { AppLocale } from "@/i18n/app-locales"
import { queryClient } from "@/lib/query-client"
export const Route = createRootRoute({
loader: () => getUiState(),
loader: async () => {
const [initialState, initialLocale] = await Promise.all([
getUiState(),
getAppLocale(),
])
return { initialLocale, initialState }
},
head: () => ({
meta: [
{ charSet: "utf-8" },
@@ -48,11 +57,11 @@ export const Route = createRootRoute({
})
function RootComponent() {
const initialState = Route.useLoaderData()
const { initialLocale, initialState } = Route.useLoaderData()
return (
<RootDocument initialState={initialState}>
<AppI18nProvider>
<RootDocument initialLocale={initialLocale} initialState={initialState}>
<AppI18nProvider locale={initialLocale}>
<BreakpointReactor />
<UiStateProvider
initialState={initialState}
@@ -94,9 +103,11 @@ function getUiInitializationScript(state: UiState) {
function RootDocument({
children,
initialLocale,
initialState,
}: Readonly<{
children: ReactNode
initialLocale: AppLocale
initialState: UiState
}>) {
const themeMode = initialState["theme-mode"]
@@ -110,7 +121,7 @@ function RootDocument({
return (
<html
lang="zh-CN"
lang={initialLocale === "zh-Hans" ? "zh-CN" : "en-US"}
suppressHydrationWarning
className={className || undefined}
style={themeMode === "system" ? undefined : { colorScheme: themeMode }}
+2
View File
@@ -8,6 +8,7 @@ import {
fetchMockNotifications,
notificationsQueryKey,
} from "@/data/notifications"
import { AppLanguageSwitcher } from "@/i18n/app-language-switcher"
export const Route = createFileRoute("/_app")({
component: ApplicationLayoutRoute,
@@ -17,6 +18,7 @@ function ApplicationLayoutRoute() {
return (
<AppLayout
chatThreads={chatThreads}
headerActions={<AppLanguageSwitcher />}
navigationGroups={primaryNavigationGroups}
notifications={{
queryFn: fetchMockNotifications,