added internationalization and some routes

This commit is contained in:
Ilgeldi 2025-02-01 14:47:48 +05:00
parent 257f9cb178
commit 37ba7e0406
13 changed files with 220 additions and 46 deletions

View File

@ -10,7 +10,7 @@ const compat = new FlatCompat({
}); });
const eslintConfig = [ const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"), ...compat.extends("next/core-web-vitals", "next/typescript",),
]; ];
export default eslintConfig; export default eslintConfig;

View File

@ -1,7 +1,8 @@
import type { NextConfig } from "next"; import createNextIntlPlugin from "next-intl/plugin";
const nextConfig: NextConfig = { const withNextIntl = createNextIntlPlugin();
/* config options here */
};
export default nextConfig; /** @type {import('next').NextConfig} */
const nextConfig = {};
export default withNextIntl(nextConfig);

113
package-lock.json generated
View File

@ -9,6 +9,7 @@
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"next": "15.1.6", "next": "15.1.6",
"next-intl": "^3.26.3",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0" "react-dom": "^19.0.0"
}, },
@ -175,6 +176,57 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
} }
}, },
"node_modules/@formatjs/ecma402-abstract": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.2.tgz",
"integrity": "sha512-6sE5nyvDloULiyOMbOTJEEgWL32w+VHkZQs8S02Lnn8Y/O5aQhjOEXwWzvR7SsBE/exxlSpY2EsWZgqHbtLatg==",
"license": "MIT",
"dependencies": {
"@formatjs/fast-memoize": "2.2.6",
"@formatjs/intl-localematcher": "0.5.10",
"decimal.js": "10",
"tslib": "2"
}
},
"node_modules/@formatjs/fast-memoize": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.6.tgz",
"integrity": "sha512-luIXeE2LJbQnnzotY1f2U2m7xuQNj2DA8Vq4ce1BY9ebRZaoPB1+8eZ6nXpLzsxuW5spQxr7LdCg+CApZwkqkw==",
"license": "MIT",
"dependencies": {
"tslib": "2"
}
},
"node_modules/@formatjs/icu-messageformat-parser": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.0.tgz",
"integrity": "sha512-Hp81uTjjdTk3FLh/dggU5NK7EIsVWc5/ZDWrIldmf2rBuPejuZ13CZ/wpVE2SToyi4EiroPTQ1XJcJuZFIxTtw==",
"license": "MIT",
"dependencies": {
"@formatjs/ecma402-abstract": "2.3.2",
"@formatjs/icu-skeleton-parser": "1.8.12",
"tslib": "2"
}
},
"node_modules/@formatjs/icu-skeleton-parser": {
"version": "1.8.12",
"resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.12.tgz",
"integrity": "sha512-QRAY2jC1BomFQHYDMcZtClqHR55EEnB96V7Xbk/UiBodsuFc5kujybzt87+qj1KqmJozFhk6n4KiT1HKwAkcfg==",
"license": "MIT",
"dependencies": {
"@formatjs/ecma402-abstract": "2.3.2",
"tslib": "2"
}
},
"node_modules/@formatjs/intl-localematcher": {
"version": "0.5.10",
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.10.tgz",
"integrity": "sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q==",
"license": "MIT",
"dependencies": {
"tslib": "2"
}
},
"node_modules/@humanfs/core": { "node_modules/@humanfs/core": {
"version": "0.19.1", "version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@ -1898,6 +1950,12 @@
} }
} }
}, },
"node_modules/decimal.js": {
"version": "10.5.0",
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz",
"integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==",
"license": "MIT"
},
"node_modules/deep-is": { "node_modules/deep-is": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@ -3208,6 +3266,18 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/intl-messageformat": {
"version": "10.7.14",
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.14.tgz",
"integrity": "sha512-mMGnE4E1otdEutV5vLUdCxRJygHB5ozUBxsPB5qhitewssrS/qGruq9bmvIRkkGsNeK5ZWLfYRld18UHGTIifQ==",
"license": "BSD-3-Clause",
"dependencies": {
"@formatjs/ecma402-abstract": "2.3.2",
"@formatjs/fast-memoize": "2.2.6",
"@formatjs/icu-messageformat-parser": "2.11.0",
"tslib": "2"
}
},
"node_modules/is-array-buffer": { "node_modules/is-array-buffer": {
"version": "3.0.5", "version": "3.0.5",
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
@ -3972,6 +4042,15 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/negotiator": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/next": { "node_modules/next": {
"version": "15.1.6", "version": "15.1.6",
"resolved": "https://registry.npmjs.org/next/-/next-15.1.6.tgz", "resolved": "https://registry.npmjs.org/next/-/next-15.1.6.tgz",
@ -4026,6 +4105,27 @@
} }
} }
}, },
"node_modules/next-intl": {
"version": "3.26.3",
"resolved": "https://registry.npmjs.org/next-intl/-/next-intl-3.26.3.tgz",
"integrity": "sha512-6Y97ODrDsEE1J8cXKMHwg1laLdtkN66QMIqG8BzH4zennJRUNTtM8UMtBDyhfmF6uiZ+xsbWLXmHUgmUymUsfQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/amannn"
}
],
"license": "MIT",
"dependencies": {
"@formatjs/intl-localematcher": "^0.5.4",
"negotiator": "^1.0.0",
"use-intl": "^3.26.3"
},
"peerDependencies": {
"next": "^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0"
}
},
"node_modules/next/node_modules/postcss": { "node_modules/next/node_modules/postcss": {
"version": "8.4.31", "version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
@ -5666,6 +5766,19 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"node_modules/use-intl": {
"version": "3.26.3",
"resolved": "https://registry.npmjs.org/use-intl/-/use-intl-3.26.3.tgz",
"integrity": "sha512-yY0a2YseO17cKwHA9M6fcpiEJ2Uo81DEU0NOUxNTp6lJVNOuI6nULANPVVht6IFdrYFtlsMmMoc97+Eq9/Tnng==",
"license": "MIT",
"dependencies": {
"@formatjs/fast-memoize": "^2.2.0",
"intl-messageformat": "^10.5.14"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0"
}
},
"node_modules/util-deprecate": { "node_modules/util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",

View File

@ -9,19 +9,20 @@
"lint": "next lint" "lint": "next lint"
}, },
"dependencies": { "dependencies": {
"next": "15.1.6",
"next-intl": "^3.26.3",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0"
"next": "15.1.6"
}, },
"devDependencies": { "devDependencies": {
"typescript": "^5", "@eslint/eslintrc": "^3",
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^19", "@types/react": "^19",
"@types/react-dom": "^19", "@types/react-dom": "^19",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "15.1.6", "eslint-config-next": "15.1.6",
"@eslint/eslintrc": "^3" "postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
} }
} }

View File

@ -0,0 +1,36 @@
import type { Metadata } from "next";
import { NextIntlClientProvider } from "next-intl";
import { getMessages } from "next-intl/server";
import "../globals.css";
type Props = {
children: React.ReactNode;
params: Promise<{ locale: "en" | "ru" | "tm" }>;
};
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const locale = (await params).locale;
const message = await (await import(`../../messages/${locale}.json`)).default;
return {
title: message.meta.title,
description: message.meta.description,
};
}
export default async function RootLayout({ children, params }: Props) {
const { locale } = await params;
const messages = await getMessages();
return (
<html lang={locale}>
<body>
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
</body>
</html>
);
}

View File

@ -1,34 +0,0 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html>
);
}

17
src/i18n/request.ts Normal file
View File

@ -0,0 +1,17 @@
import { getRequestConfig } from "next-intl/server";
import { routing } from "./routing";
export default getRequestConfig(async ({ requestLocale }) => {
let locale = await requestLocale;
// Ensure that a valid locale is used
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if (!locale || !routing.locales.includes(locale as any)) {
locale = routing.defaultLocale;
}
return {
locale,
messages: (await import(`../messages/${locale}.json`)).default,
};
});

13
src/i18n/routing.ts Normal file
View File

@ -0,0 +1,13 @@
import { defineRouting } from "next-intl/routing";
import { createNavigation } from "next-intl/navigation";
export const routing = defineRouting({
// A list of all locales that are supported
locales: ["en", "tm", "ru"],
// Used when no locale matches
defaultLocale: "en",
});
export const { Link, redirect, usePathname, useRouter, getPathname } =
createNavigation(routing);

6
src/messages/en.json Normal file
View File

@ -0,0 +1,6 @@
{
"meta": {
"title": "News Orient",
"description": "News Orient"
}
}

6
src/messages/ru.json Normal file
View File

@ -0,0 +1,6 @@
{
"meta": {
"title": "Новости Orient",
"description": "Новости Orient"
}
}

6
src/messages/tm.json Normal file
View File

@ -0,0 +1,6 @@
{
"meta": {
"title": "Habarlar Orient",
"description": "Habarlar Orient"
}
}

9
src/middleware.ts Normal file
View File

@ -0,0 +1,9 @@
import createMiddleware from "next-intl/middleware";
import { routing } from "./i18n/routing";
export default createMiddleware(routing);
export const config = {
// Match only internationalized pathnames
matcher: ["/", "/(tm|en|ru)/:path*"],
};