├── .eslintrc.json
├── .gitignore
├── README.md
├── app
├── [locale]
│ ├── about
│ │ ├── page.tsx
│ │ └── profile
│ │ │ └── page.tsx
│ ├── layout.tsx
│ └── page.tsx
├── favicon.ico
├── fonts
│ ├── GeistMonoVF.woff
│ └── GeistVF.woff
└── globals.css
├── components
└── Navbar.tsx
├── i18n.ts
├── messages
├── en.json
└── fr.json
├── middleware.ts
├── next.config.mjs
├── package-lock.json
├── package.json
├── postcss.config.mjs
├── public
├── file-text.svg
├── globe.svg
├── next.svg
├── vercel.svg
└── window.svg
├── tailwind.config.ts
└── tsconfig.json
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # env files (can opt-in for commiting if needed)
29 | .env*
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Watch Tutorial: https://www.youtube.com/watch?v=2Jh9olZXBfw
2 |
3 | This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/create-next-app).
4 |
5 | ## Getting Started
6 |
7 | First, run the development server:
8 |
9 | ```bash
10 | npm run dev
11 | # or
12 | yarn dev
13 | # or
14 | pnpm dev
15 | # or
16 | bun dev
17 | ```
18 |
19 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
20 |
21 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
22 |
23 | This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load Inter, a custom Google Font.
24 |
25 | ## Learn More
26 |
27 | To learn more about Next.js, take a look at the following resources:
28 |
29 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
30 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
31 |
32 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
33 |
34 | ## Deploy on Vercel
35 |
36 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
37 |
38 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
39 |
--------------------------------------------------------------------------------
/app/[locale]/about/page.tsx:
--------------------------------------------------------------------------------
1 | import { useTranslations } from "next-intl";
2 | import { getMessages } from "next-intl/server";
3 | import React from "react";
4 |
5 | export async function generateMetadata({
6 | params: { locale },
7 | }: {
8 | params: { locale: string };
9 | }) {
10 | const messages: any = await getMessages({ locale });
11 | const title = messages.NavbarLinks.aboutTitle;
12 |
13 | return {
14 | title,
15 | };
16 | }
17 |
18 | const AboutPage = () => {
19 | const t = useTranslations("AboutPage");
20 | return (
21 |
22 |
{t("title")}
23 |
24 | );
25 | };
26 |
27 | export default AboutPage;
28 |
--------------------------------------------------------------------------------
/app/[locale]/about/profile/page.tsx:
--------------------------------------------------------------------------------
1 | import { useTranslations } from "next-intl";
2 | import { getMessages } from "next-intl/server";
3 | import React from "react";
4 |
5 | export async function generateMetadata({
6 | params: { locale },
7 | }: {
8 | params: { locale: string };
9 | }) {
10 | const messages: any = await getMessages({ locale });
11 | const title = messages.NavbarLinks.profileTitle;
12 |
13 | return {
14 | title,
15 | };
16 | }
17 |
18 | const ProfilePage = () => {
19 | const t = useTranslations("ProfilePage");
20 | return (
21 |
22 |
{t("title")}
23 |
24 | );
25 | };
26 |
27 | export default ProfilePage;
28 |
--------------------------------------------------------------------------------
/app/[locale]/layout.tsx:
--------------------------------------------------------------------------------
1 | import Navbar from "@/components/Navbar";
2 | import "../globals.css";
3 | import { NextIntlClientProvider } from "next-intl";
4 | import { getMessages } from "next-intl/server";
5 |
6 | export default async function RootLayout({
7 | children,
8 | params: { locale },
9 | }: Readonly<{
10 | children: React.ReactNode;
11 | params: { locale: string };
12 | }>) {
13 | const messages = await getMessages();
14 | return (
15 |
16 |
17 |
18 |
19 |
20 | {children}
21 |
22 |
23 |
24 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/app/[locale]/page.tsx:
--------------------------------------------------------------------------------
1 | import { useTranslations } from "next-intl";
2 | import { getMessages } from "next-intl/server";
3 |
4 | export async function generateMetadata({
5 | params: { locale },
6 | }: {
7 | params: { locale: string };
8 | }) {
9 | const messages: any = await getMessages({ locale });
10 | const title = messages.NavbarLinks.homeTitle;
11 |
12 | return {
13 | title,
14 | };
15 | }
16 |
17 | export default function Home() {
18 | const t = useTranslations("HomePage");
19 | return (
20 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umairjameel321/next15-intl-i18n/3b68bd2c2d64501e68a46bfc4a3e73de480d5944/app/favicon.ico
--------------------------------------------------------------------------------
/app/fonts/GeistMonoVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umairjameel321/next15-intl-i18n/3b68bd2c2d64501e68a46bfc4a3e73de480d5944/app/fonts/GeistMonoVF.woff
--------------------------------------------------------------------------------
/app/fonts/GeistVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umairjameel321/next15-intl-i18n/3b68bd2c2d64501e68a46bfc4a3e73de480d5944/app/fonts/GeistVF.woff
--------------------------------------------------------------------------------
/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | :root {
6 | --background: #ffffff;
7 | --foreground: #171717;
8 | }
9 |
10 | @media (prefers-color-scheme: dark) {
11 | :root {
12 | --background: #0a0a0a;
13 | --foreground: #ededed;
14 | }
15 | }
16 |
17 | body {
18 | color: var(--foreground);
19 | background: var(--background);
20 | font-synthesis: none;
21 | }
22 |
--------------------------------------------------------------------------------
/components/Navbar.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useTranslations } from "next-intl";
4 | import Link from "next/link";
5 | import { usePathname, useRouter } from "next/navigation";
6 | import React, { ChangeEvent } from "react";
7 |
8 | const Navbar = ({ locale }: { locale: string }) => {
9 | const t = useTranslations("NavbarLinks");
10 | const pathname = usePathname();
11 | const router = useRouter();
12 |
13 | const handleLanguageChange = (e: ChangeEvent) => {
14 | const newLocale = e.target.value as string;
15 | const path = pathname.split("/").slice(2).join("/");
16 | router.push(`/${newLocale}/${path}`);
17 | };
18 | return (
19 |
20 |
21 | {t("home")}
22 | {t("about")}
23 | {t("profile")}
24 |
25 |
33 |
34 | );
35 | };
36 |
37 | export default Navbar;
38 |
--------------------------------------------------------------------------------
/i18n.ts:
--------------------------------------------------------------------------------
1 | import { notFound } from "next/navigation";
2 | import { getRequestConfig } from "next-intl/server";
3 |
4 | const locales = ["en", "fr"];
5 |
6 | export default getRequestConfig(async ({ locale }) => {
7 | if (!locales.includes(locale as any)) notFound();
8 |
9 | return {
10 | messages: (await import(`./messages/${locale}.json`)).default,
11 | };
12 | });
13 |
--------------------------------------------------------------------------------
/messages/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "NavbarLinks": {
3 | "home": "Home",
4 | "homeTitle": "Home page",
5 | "about": "About",
6 | "aboutTitle": "About page",
7 | "profile": "Profile",
8 | "profileTitle": "Profile page"
9 | },
10 | "HomePage": {
11 | "title": "Landing page"
12 | },
13 | "AboutPage": {
14 | "title": "About"
15 | },
16 | "ProfilePage": {
17 | "title": "Profile"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/messages/fr.json:
--------------------------------------------------------------------------------
1 | {
2 | "NavbarLinks": {
3 | "home": "Accueil",
4 | "homeTitle": "Page d'accueil",
5 | "about": "À propos",
6 | "aboutTitle": "Page d'à propos",
7 | "profile": "Profil",
8 | "profileTitle": "Page de profil"
9 | },
10 | "HomePage": {
11 | "title": "Page d'atterrissage"
12 | },
13 | "AboutPage": {
14 | "title": "À propos"
15 | },
16 | "ProfilePage": {
17 | "title": "Profil"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/middleware.ts:
--------------------------------------------------------------------------------
1 | import createMiddleware from "next-intl/middleware";
2 |
3 | export default createMiddleware({
4 | locales: ["en", "fr"],
5 | defaultLocale: "en",
6 | });
7 |
8 | export const config = {
9 | matcher: ["/", "/(fr|en)/:path*"],
10 | };
11 |
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 |
3 | import createNextIntlPlugin from "next-intl/plugin";
4 |
5 | const withNextIntl = createNextIntlPlugin();
6 | const nextConfig = {};
7 |
8 | export default withNextIntl(nextConfig);
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "next15-langs",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "next": "15.0.0-rc.0",
13 | "next-intl": "^3.17.2",
14 | "react": "19.0.0-rc-f994737d14-20240522",
15 | "react-dom": "19.0.0-rc-f994737d14-20240522"
16 | },
17 | "devDependencies": {
18 | "@types/node": "^20",
19 | "@types/react": "^18",
20 | "@types/react-dom": "^18",
21 | "eslint": "^8",
22 | "eslint-config-next": "15.0.0-rc.0",
23 | "postcss": "^8",
24 | "tailwindcss": "^3.4.1",
25 | "typescript": "^5"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/public/file-text.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/public/globe.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/public/window.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | const config: Config = {
4 | content: [
5 | "./pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | colors: {
12 | background: "var(--background)",
13 | foreground: "var(--foreground)",
14 | },
15 | fontFamily: {
16 | sans: ["var(--font-geist-sans)"],
17 | mono: ["var(--font-geist-mono)"],
18 | },
19 | },
20 | },
21 | plugins: [],
22 | };
23 | export default config;
24 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2017",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------