feat: implement internationalization and routing for locale-based pages

- Added RootLayout component to handle locale and message loading.
- Created Home page with header, about info, products, and story sections.
- Developed ProductPage for displaying product categories and products.
- Implemented ProductsPage to list product categories with banners.
- Added Projects page with a grid layout for project display.
- Configured server settings for port and host.
- Established navigation and routing for internationalization.
- Set up request handling for locale and message retrieval.
- Defined routing structure for supported locales.
- Created middleware for locale-based routing.
- Defined layout parameters for locale handling.
This commit is contained in:
2025-06-07 16:31:52 +03:30
parent 6728b99e33
commit a3915377bd
44 changed files with 775 additions and 324 deletions
+30
View File
@@ -0,0 +1,30 @@
import images from '@/assets/images/images';
import InnerPageBanner from '@/components/layout/innerPages/banner';
import AboutTopInfo from '@/components/pages/about/TopInfo';
import { ILayoutLocaleParams } from '@/models/layout';
import { useTranslations } from 'next-intl';
import { setRequestLocale } from 'next-intl/server';
import { getTranslations } from 'next-intl/server';
export default async function AboutUs({
params,
}: Readonly<{
params: ILayoutLocaleParams;
}>) {
const { locale } = await params;
// Enable static rendering
setRequestLocale(locale);
const t = await getTranslations({ locale });
return (
<main className="flex flex-col items-center justify-between">
<InnerPageBanner
title={t('pages_about_title')}
imageSrc={images.aboutBanner.src}
/>
<AboutTopInfo />
</main>
);
}
+24
View File
@@ -0,0 +1,24 @@
import images from '@/assets/images/images';
import InnerPageBanner from '@/components/layout/innerPages/banner';
import PostsGrid from '@/components/pages/blog/PostsGrid';
import { ILayoutLocaleParams } from '@/models/layout';
import { getTranslations } from 'next-intl/server';
export default async function Blog({
params,
}: {
params: ILayoutLocaleParams;
}) {
const t = await getTranslations({ locale: params.locale });
return (
<main className="flex flex-col items-center justify-between">
<InnerPageBanner
title={t('pages_blog_title')}
imageSrc={images.blogBanner.src}
/>
<PostsGrid />
</main>
);
}
+44
View File
@@ -0,0 +1,44 @@
import images from '@/assets/images/images';
import InnerPageBanner from '@/components/layout/innerPages/banner';
import ContactMap from '@/components/layout/map';
import ContactForm from '@/components/pages/contact/ContactMeForm';
import TouchUs from '@/components/pages/contact/TouchUs';
import Image from 'next/image';
import { getTranslations } from 'next-intl/server';
import { ILayoutLocaleParams } from '@/models/layout';
export default async function Contact({
params,
}: {
params: ILayoutLocaleParams;
}) {
const t = await getTranslations({ locale: params.locale });
return (
<main className="flex flex-col items-center justify-between">
<InnerPageBanner
title={t('pages_contact_title')}
imageSrc={images.blogBanner.src}
/>
<div className="flex w-full flex-col bg-gray-200 pt-4 md:pt-28">
<div className="relative container mx-auto flex w-full flex-col items-stretch overflow-hidden rounded-lg md:flex-row">
<TouchUs />
<ContactForm />
</div>
<div className="absolute -bottom-[17.32rem] w-1/2 max-md:hidden md:w-2/5">
<Image
alt="footer-bg"
src={images.factoryVector}
className="h-full w-full object-cover"
/>
</div>
<div className="mt-4 w-full md:mt-28">
<div className="h-64 w-full overflow-hidden rounded-lg bg-gray-200">
<ContactMap />
</div>
</div>
</div>
</main>
);
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+43
View File
@@ -0,0 +1,43 @@
import Footer from '@/components/layout/footer/Footer';
import '@/app/globals.css';
import 'leaflet/dist/leaflet.css';
import { notFound } from 'next/navigation';
import { NextIntlClientProvider } from 'next-intl';
import type { ILayoutLocaleParams } from '@/models/layout.d';
// export const metadata: Metadata = {
// title: 'Create Next App',
// description: 'Generated by create next app',
// };
export default async function RootLayout({
children,
params,
}: Readonly<{
children: React.ReactNode;
params: ILayoutLocaleParams;
}>) {
const locale = params.locale;
console.log(locale);
let messages;
try {
console.log(await import(`../../../messages/en.json`));
messages = (await import(`../../../messages/en.json`)).default;
} catch (error) {
console.log(error);
notFound();
}
return (
<html lang={locale} dir={locale === 'fa' ? 'rtl' : 'ltr'}>
<body className="min-h-svh w-full overflow-x-hidden">
<NextIntlClientProvider locale={locale} messages={messages}>
{children}
<Footer />
</NextIntlClientProvider>
</body>
</html>
);
}
+25
View File
@@ -0,0 +1,25 @@
import Header from '@/components/layout/header';
import AboutTopInfo from '@/components/pages/about/TopInfo';
import HomeStorySection from '@/components/pages/home/HomeStorySection';
import HomePageProducts from '@/components/pages/home/Products';
import { ILayoutLocaleParams } from '@/models/layout';
import { getTranslations } from 'next-intl/server';
interface IPageParams {
params: ILayoutLocaleParams;
}
export default async function Home({ params }: IPageParams) {
const { locale } = await params;
const t = await getTranslations({ locale });
return (
<main className="flex flex-col items-center justify-between">
<Header />
<AboutTopInfo />
<HomePageProducts locale={locale} />
<HomeStorySection className="my-24" />
</main>
);
}
@@ -0,0 +1,61 @@
import routeFactory from '@/assets/constants/routeFactory';
import images from '@/assets/images/images';
import Breadcrumb from '@/components/layout/breadcrumb';
import { IBreadcrumbItem } from '@/components/layout/breadcrumb/breadcrumb';
import InnerPageBanner from '@/components/layout/innerPages/banner';
import ProductCategoryCardInProducts from '@/components/pages/productCategories/ProductCategoryCardInProducts';
import ProductCard from '@/components/pages/products/ProductCard';
import { ILayoutLocaleParams } from '@/models/layout';
import { getProductCategoryById, getProducts } from '@/services/products';
import { getTranslations } from 'next-intl/server';
interface IParams extends ILayoutLocaleParams {
product_category_id: string;
}
export default async function ProductPage({
params,
}: {
params: Promise<IParams>;
}) {
const { product_category_id: productCategoryId, locale } = await params;
const t = await getTranslations({ locale });
const routes = routeFactory(t, locale);
const productCategory = await getProductCategoryById(productCategoryId);
const products = await getProducts(productCategoryId);
const breadcrumbItems = [
{
...routes.productCategories,
href: routes.productCategories.route(),
},
{ title: productCategory.title },
] as IBreadcrumbItem[];
return (
<main className="">
<InnerPageBanner
title={productCategory.title}
imageSrc={images.homeProductBack.src}
/>
<div className="container-fluid mx-auto my-24">
<Breadcrumb items={breadcrumbItems} />
<ProductCategoryCardInProducts
productCategory={productCategory}
className="mt-6"
/>
<div className="mt-20">
<h2 className="mb-6 text-3xl font-semibold text-gray-500">
{productCategory.title} Products
</h2>
<div className="grid gap-3 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5">
{products.map((product, index) => (
<ProductCard {...product} key={index} />
))}
</div>
</div>
</div>
</main>
);
}
@@ -0,0 +1,42 @@
import images from '@/assets/images/images';
import InnerPageBanner from '@/components/layout/innerPages/banner';
import ProductsCategoryCard from '@/components/pages/productCategories/ProductCategoryCard';
import type { IProductCategoryCardProps } from '@/components/pages/productCategories/productCategoryCard.d';
import { ILayoutLocaleParams } from '@/models/layout';
import { getProductCategories } from '@/services/products';
import { getTranslations } from 'next-intl/server';
export default async function ProductsPage({
params,
}: {
params: ILayoutLocaleParams;
}) {
const awaitedParams = await params;
const t = await getTranslations({ locale: awaitedParams.locale });
const productCategories: IProductCategoryCardProps[] =
await getProductCategories();
return (
<main>
<InnerPageBanner
title={t('pages_product_categories_title')}
imageSrc={images.aboutBanner.src}
/>
<div className="container-fluid mx-auto my-24">
<div className="grid w-full grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4">
{productCategories.map((category) => (
<ProductsCategoryCard key={category.id} {...category} />
))}
</div>
<div className="mt-20">
<h2 className="mb-4 text-2xl font-semibold text-gray-500">
{t('pages_products_content_title')}
</h2>
<p className="text-base font-light text-gray-400">
{t('pages_products_content_description')}
</p>
</div>
</div>
</main>
);
}
+24
View File
@@ -0,0 +1,24 @@
import images from '@/assets/images/images';
import InnerPageBanner from '@/components/layout/innerPages/banner';
import ProjectsGrid from '@/components/pages/project/ProjectGrid';
import { ILayoutLocaleParams } from '@/models/layout';
import { getTranslations } from 'next-intl/server';
export default async function Projects({
params,
}: {
params: ILayoutLocaleParams;
}) {
const t = await getTranslations({ locale: params.locale });
return (
<main className="flex flex-col items-center justify-between">
<InnerPageBanner
title={t('pages_projects_title')}
imageSrc={images.blogBanner.src}
/>
<ProjectsGrid />
</main>
);
}