feat: configure next-intl i18n with ES/EN locales

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
consultoria-as
2026-02-22 03:54:05 +00:00
parent 6f342a065b
commit 00d500d3d3
12 changed files with 1322 additions and 637 deletions

View File

@@ -1,5 +1,7 @@
import type { NextConfig } from "next";
import createNextIntlPlugin from "next-intl/plugin";
const nextConfig: NextConfig = {};
const withNextIntl = createNextIntlPlugin("./src/i18n/request.ts");
export default nextConfig;
const nextConfig = {};
export default withNextIntl(nextConfig);

View File

@@ -9,19 +9,20 @@
"lint": "next lint"
},
"dependencies": {
"@afterlife/shared": "*",
"next": "^15",
"next-intl": "^4.8.3",
"react": "^19",
"react-dom": "^19",
"@afterlife/shared": "*"
"react-dom": "^19"
},
"devDependencies": {
"typescript": "^5",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"tailwindcss": "^4",
"@tailwindcss/postcss": "^4",
"eslint": "^9",
"eslint-config-next": "^15"
"eslint-config-next": "^15",
"tailwindcss": "^4",
"typescript": "^5"
}
}

View File

@@ -0,0 +1,35 @@
import type { Metadata } from "next";
import { NextIntlClientProvider } from "next-intl";
import { getMessages } from "next-intl/server";
import { notFound } from "next/navigation";
import { routing } from "@/i18n/routing";
import "./globals.css";
export const metadata: Metadata = {
title: "Project Afterlife",
description: "Preserving online games that deserve a second life",
};
export default async function LocaleLayout({
children,
params,
}: {
children: React.ReactNode;
params: Promise<{ locale: string }>;
}) {
const { locale } = await params;
if (!routing.locales.includes(locale as any)) {
notFound();
}
const messages = await getMessages();
return (
<html lang={locale}>
<body>
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
</body>
</html>
);
}

View File

@@ -1,19 +0,0 @@
import type { Metadata } from "next";
import "./globals.css";
export const metadata: Metadata = {
title: "Project Afterlife",
description: "Preserving online games that deserve a second life",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="es">
<body>{children}</body>
</html>
);
}

View File

@@ -0,0 +1,13 @@
import { getRequestConfig } from "next-intl/server";
import { routing } from "./routing";
export default getRequestConfig(async ({ requestLocale }) => {
let locale = await requestLocale;
if (!locale || !routing.locales.includes(locale as any)) {
locale = routing.defaultLocale;
}
return {
locale,
messages: (await import(`../messages/${locale}.json`)).default,
};
});

View File

@@ -0,0 +1,6 @@
import { defineRouting } from "next-intl/routing";
export const routing = defineRouting({
locales: ["es", "en"],
defaultLocale: "es",
});

View File

@@ -0,0 +1,63 @@
{
"nav": {
"home": "Home",
"catalog": "Catalog",
"about": "About Us",
"donate": "Donations"
},
"home": {
"hero_title": "Project Afterlife",
"hero_subtitle": "Preserving online games that deserve a second life",
"latest_games": "Latest restored games",
"view_all": "View all",
"donate_cta": "Support preservation"
},
"catalog": {
"title": "Game Catalog",
"filter_genre": "Genre",
"filter_status": "Server status",
"all": "All",
"no_results": "No games found"
},
"game": {
"released": "Released",
"shutdown": "Shutdown",
"developer": "Developer",
"publisher": "Publisher",
"server_status": "Server status",
"play_now": "Play now",
"view_documentary": "View documentary",
"status_online": "Online",
"status_maintenance": "Maintenance",
"status_coming_soon": "Coming soon"
},
"documentary": {
"chapters": "Chapters",
"listen": "Listen",
"reading_progress": "Reading progress"
},
"audio": {
"play": "Play",
"pause": "Pause",
"speed": "Speed",
"chapter_mode": "By chapter",
"continuous_mode": "Continuous"
},
"about": {
"title": "About Us",
"mission": "Our Mission",
"team": "The Team",
"contribute": "How to Contribute"
},
"donate": {
"title": "Donations",
"description": "Project Afterlife is funded exclusively by donations. Your support keeps these games alive.",
"patreon": "Donate on Patreon",
"kofi": "Donate on Ko-fi",
"transparency": "Fund transparency"
},
"footer": {
"rights": "Project Afterlife. Preserving gaming history.",
"language": "Language"
}
}

View File

@@ -0,0 +1,63 @@
{
"nav": {
"home": "Inicio",
"catalog": "Catálogo",
"about": "Sobre Nosotros",
"donate": "Donaciones"
},
"home": {
"hero_title": "Project Afterlife",
"hero_subtitle": "Preservando juegos online que merecen una segunda vida",
"latest_games": "Últimos juegos restaurados",
"view_all": "Ver todos",
"donate_cta": "Apoya la preservación"
},
"catalog": {
"title": "Catálogo de Juegos",
"filter_genre": "Género",
"filter_status": "Estado del servidor",
"all": "Todos",
"no_results": "No se encontraron juegos"
},
"game": {
"released": "Lanzado",
"shutdown": "Cerrado",
"developer": "Desarrolladora",
"publisher": "Distribuidora",
"server_status": "Estado del servidor",
"play_now": "Jugar ahora",
"view_documentary": "Ver documental",
"status_online": "En línea",
"status_maintenance": "Mantenimiento",
"status_coming_soon": "Próximamente"
},
"documentary": {
"chapters": "Capítulos",
"listen": "Escuchar",
"reading_progress": "Progreso de lectura"
},
"audio": {
"play": "Reproducir",
"pause": "Pausar",
"speed": "Velocidad",
"chapter_mode": "Por capítulo",
"continuous_mode": "Continuo"
},
"about": {
"title": "Sobre Nosotros",
"mission": "Nuestra Misión",
"team": "El Equipo",
"contribute": "Cómo Contribuir"
},
"donate": {
"title": "Donaciones",
"description": "Project Afterlife se financia exclusivamente con donaciones. Tu apoyo mantiene vivos estos juegos.",
"patreon": "Donar en Patreon",
"kofi": "Donar en Ko-fi",
"transparency": "Transparencia de fondos"
},
"footer": {
"rights": "Project Afterlife. Preservando la historia del gaming.",
"language": "Idioma"
}
}

View File

@@ -0,0 +1,8 @@
import createMiddleware from "next-intl/middleware";
import { routing } from "./i18n/routing";
export default createMiddleware(routing);
export const config = {
matcher: ["/((?!api|_next|_vercel|.*\\..*).*)"],
};

1731
package-lock.json generated

File diff suppressed because it is too large Load Diff