fix(web): wire up next-intl i18n — install dep, add locale middleware, wrap next config
The i18n architecture (config, routing, translation files, locale pages) was already built but non-functional due to three missing pieces: 1. next-intl not listed in package.json 2. middleware.ts not using createMiddleware from next-intl/middleware 3. next.config.js not wrapped with createNextIntlPlugin Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -1,32 +1,45 @@
|
||||
import { NextResponse, type NextRequest } from 'next/server';
|
||||
import createIntlMiddleware from 'next-intl/middleware';
|
||||
import { routing } from '@/i18n/routing';
|
||||
|
||||
const intlMiddleware = createIntlMiddleware(routing);
|
||||
|
||||
const publicPaths = ['/login', '/register', '/search', '/auth/callback'];
|
||||
|
||||
const publicExactPaths = ['/'];
|
||||
const authOnlyPaths = ['/login', '/register'];
|
||||
|
||||
function isPublicPath(pathname: string): boolean {
|
||||
return (
|
||||
publicExactPaths.includes(pathname) ||
|
||||
publicPaths.some((path) => pathname.startsWith(path))
|
||||
);
|
||||
}
|
||||
|
||||
function stripLocale(pathname: string): string {
|
||||
const localePattern = /^\/(vi|en)(\/|$)/;
|
||||
return pathname.replace(localePattern, '/') || '/';
|
||||
}
|
||||
|
||||
export function middleware(request: NextRequest) {
|
||||
const { pathname } = request.nextUrl;
|
||||
const strippedPath = stripLocale(pathname);
|
||||
|
||||
const isPublicPath =
|
||||
publicExactPaths.includes(pathname) ||
|
||||
publicPaths.some((path) => pathname.startsWith(path));
|
||||
|
||||
// We check for the token cookie or rely on client-side auth store.
|
||||
// For SSR-safe auth, check a lightweight cookie set by the client after login.
|
||||
const hasAuthCookie = request.cookies.has('goodgo_authenticated');
|
||||
|
||||
if (!isPublicPath && !hasAuthCookie) {
|
||||
if (!isPublicPath(strippedPath) && !hasAuthCookie) {
|
||||
const loginUrl = new URL('/login', request.url);
|
||||
loginUrl.searchParams.set('redirect', pathname);
|
||||
loginUrl.searchParams.set('redirect', strippedPath);
|
||||
return NextResponse.redirect(loginUrl);
|
||||
}
|
||||
|
||||
const isAuthOnlyPath = ['/login', '/register'].some((path) => pathname.startsWith(path));
|
||||
if (isAuthOnlyPath && hasAuthCookie) {
|
||||
const isAuthOnly = authOnlyPaths.some((path) =>
|
||||
strippedPath.startsWith(path),
|
||||
);
|
||||
if (isAuthOnly && hasAuthCookie) {
|
||||
return NextResponse.redirect(new URL('/dashboard', request.url));
|
||||
}
|
||||
|
||||
return NextResponse.next();
|
||||
return intlMiddleware(request);
|
||||
}
|
||||
|
||||
export const config = {
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
const { withSentryConfig } = require('@sentry/nextjs');
|
||||
const createNextIntlPlugin = require('next-intl/plugin');
|
||||
|
||||
const withNextIntl = createNextIntlPlugin('./i18n/request.ts');
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
@@ -44,7 +47,7 @@ const nextConfig = {
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = withSentryConfig(nextConfig, {
|
||||
module.exports = withSentryConfig(withNextIntl(nextConfig), {
|
||||
org: process.env.SENTRY_ORG,
|
||||
project: process.env.SENTRY_PROJECT,
|
||||
silent: !process.env.CI,
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"lucide-react": "^1.7.0",
|
||||
"mapbox-gl": "^3.21.0",
|
||||
"next": "^14.2.0",
|
||||
"next-intl": "^4.9.0",
|
||||
"react": "^18.3.0",
|
||||
"react-dom": "^18.3.0",
|
||||
"react-hook-form": "^7.72.1",
|
||||
|
||||
Reference in New Issue
Block a user