feat: localization service and frontend integration
- Add localization-service microservice (locale resolution, translations) - Add frontend API routes /api/locale and /api/translations/[locale] - Add LocalizationProvider and localization context - Integrate localization into layout, EmptyChat, MessageInput components - Update MICROSERVICES.md architecture docs - Add localization-service to workspaces Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
55
apps/frontend/src/app/api/locale/route.ts
Normal file
55
apps/frontend/src/app/api/locale/route.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
const LOCALIZATION_SERVICE_URL =
|
||||
process.env.LOCALIZATION_SERVICE_URL ?? 'http://localhost:4003';
|
||||
|
||||
const fallbackLocale = {
|
||||
locale: 'en',
|
||||
language: 'en',
|
||||
region: null,
|
||||
countryCode: null,
|
||||
timezone: null,
|
||||
source: 'fallback' as const,
|
||||
};
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
try {
|
||||
const res = await fetch(`${LOCALIZATION_SERVICE_URL}/api/locale`, {
|
||||
headers: {
|
||||
'x-forwarded-for': req.headers.get('x-forwarded-for') ?? '',
|
||||
'x-real-ip': req.headers.get('x-real-ip') ?? '',
|
||||
'user-agent': req.headers.get('user-agent') ?? '',
|
||||
'accept-language': req.headers.get('accept-language') ?? '',
|
||||
},
|
||||
});
|
||||
|
||||
if (!res.ok) return NextResponse.json(fallbackLocale);
|
||||
const data = await res.json();
|
||||
return NextResponse.json(data);
|
||||
} catch {
|
||||
return NextResponse.json(fallbackLocale);
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
try {
|
||||
const body = await req.json();
|
||||
const res = await fetch(`${LOCALIZATION_SERVICE_URL}/api/locale`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-forwarded-for': req.headers.get('x-forwarded-for') ?? '',
|
||||
'x-real-ip': req.headers.get('x-real-ip') ?? '',
|
||||
'user-agent': req.headers.get('user-agent') ?? '',
|
||||
'accept-language': req.headers.get('accept-language') ?? '',
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
if (!res.ok) return NextResponse.json(fallbackLocale);
|
||||
const data = await res.json();
|
||||
return NextResponse.json(data);
|
||||
} catch {
|
||||
return NextResponse.json(fallbackLocale);
|
||||
}
|
||||
}
|
||||
37
apps/frontend/src/app/api/translations/[locale]/route.ts
Normal file
37
apps/frontend/src/app/api/translations/[locale]/route.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
const LOCALIZATION_SERVICE_URL =
|
||||
process.env.LOCALIZATION_SERVICE_URL ?? 'http://localhost:4003';
|
||||
|
||||
export async function GET(
|
||||
req: NextRequest,
|
||||
{ params }: { params: Promise<{ locale: string }> },
|
||||
) {
|
||||
try {
|
||||
const { locale } = await params;
|
||||
if (!locale) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Locale is required' },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
const res = await fetch(
|
||||
`${LOCALIZATION_SERVICE_URL}/api/translations/${encodeURIComponent(locale)}`,
|
||||
);
|
||||
|
||||
if (!res.ok) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to fetch translations' },
|
||||
{ status: 502 },
|
||||
);
|
||||
}
|
||||
const data = await res.json();
|
||||
return NextResponse.json(data);
|
||||
} catch {
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to fetch translations' },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { cn } from '@/lib/utils';
|
||||
import Sidebar from '@/components/Sidebar';
|
||||
import { Toaster } from 'sonner';
|
||||
import ThemeProvider from '@/components/theme/Provider';
|
||||
import { LocalizationProvider } from '@/lib/localization/context';
|
||||
import configManager from '@/lib/config';
|
||||
import SetupWizard from '@/components/Setup/SetupWizard';
|
||||
import { ChatProvider } from '@/lib/hooks/useChat';
|
||||
@@ -36,8 +37,9 @@ export default function RootLayout({
|
||||
<html className="h-full" lang="en" suppressHydrationWarning>
|
||||
<body className={cn('h-full antialiased', roboto.className)}>
|
||||
<ThemeProvider>
|
||||
{setupComplete ? (
|
||||
<ChatProvider>
|
||||
<LocalizationProvider>
|
||||
{setupComplete ? (
|
||||
<ChatProvider>
|
||||
<Sidebar>{children}</Sidebar>
|
||||
<Toaster
|
||||
toastOptions={{
|
||||
@@ -48,10 +50,11 @@ export default function RootLayout({
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</ChatProvider>
|
||||
) : (
|
||||
<SetupWizard configSections={configSections} />
|
||||
)}
|
||||
</ChatProvider>
|
||||
) : (
|
||||
<SetupWizard configSections={configSections} />
|
||||
)}
|
||||
</LocalizationProvider>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user