nternationalization added to website

This commit is contained in:
parsa aghaei 2024-10-15 16:40:42 +03:30
parent 1f5b7f3037
commit 013f49be1e
131 changed files with 359 additions and 36 deletions

34
package-lock.json generated
View File

@ -8,6 +8,7 @@
"name": "parsaaghayi-front", "name": "parsaaghayi-front",
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"cookies-next": "^4.3.0",
"next": "14.2.11", "next": "14.2.11",
"react": "^18", "react": "^18",
"react-dom": "^18", "react-dom": "^18",
@ -17,6 +18,7 @@
}, },
"devDependencies": { "devDependencies": {
"@next/bundle-analyzer": "^14.2.13", "@next/bundle-analyzer": "^14.2.13",
"@types/negotiator": "^0.6.3",
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^18", "@types/react": "^18",
"@types/react-dom": "^18", "@types/react-dom": "^18",
@ -913,6 +915,12 @@
"tslib": "^2.4.0" "tslib": "^2.4.0"
} }
}, },
"node_modules/@types/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==",
"license": "MIT"
},
"node_modules/@types/json-schema": { "node_modules/@types/json-schema": {
"version": "7.0.15", "version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@ -927,6 +935,13 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/@types/negotiator/-/negotiator-0.6.3.tgz",
"integrity": "sha512-JkXTOdKs5MF086b/pt8C3+yVp3iDUwG635L7oCH6HvJvvr6lSUU5oe/gLXnPEfYRROHjJIPgCV6cuAg8gGkntQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.16.5", "version": "20.16.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.5.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.5.tgz",
@ -1760,6 +1775,25 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/cookie": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookies-next": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/cookies-next/-/cookies-next-4.3.0.tgz",
"integrity": "sha512-XxeCwLR30cWwRd94sa9X5lRCDLVujtx73tv+N0doQCFIDl83fuuYdxbu/WQUt9aSV7EJx7bkMvJldjvzuFqr4w==",
"license": "MIT",
"dependencies": {
"@types/cookie": "^0.6.0",
"cookie": "^0.7.0"
}
},
"node_modules/cross-env": { "node_modules/cross-env": {
"version": "7.0.3", "version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",

View File

@ -3,7 +3,7 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "start cmd.exe /k \"C:/laragon/bin/mysql/mysql-8.0.30-winx64/bin/mysqld.exe\" && start cmd.exe /k \"cd C:/Users/p.aghaei/Desktop/Work/parsa/parsaaghayi/backend && php artisan serve\" && start cmd.exe /k \"cd C:/Users/p.aghaei/Desktop/Work/parsa/parsaaghayi/frontend && next dev\"",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "next lint", "lint": "next lint",
@ -11,6 +11,7 @@
"analyze": "cross-env ANALYZE=true next build" "analyze": "cross-env ANALYZE=true next build"
}, },
"dependencies": { "dependencies": {
"cookies-next": "^4.3.0",
"next": "14.2.11", "next": "14.2.11",
"react": "^18", "react": "^18",
"react-dom": "^18", "react-dom": "^18",
@ -20,6 +21,7 @@
}, },
"devDependencies": { "devDependencies": {
"@next/bundle-analyzer": "^14.2.13", "@next/bundle-analyzer": "^14.2.13",
"@types/negotiator": "^0.6.3",
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^18", "@types/react": "^18",
"@types/react-dom": "^18", "@types/react-dom": "^18",

View File

@ -1,31 +1,43 @@
import siteTexts from "@/app/_constants/siteTexts";
import React from "react"; import React from "react";
export default function Contact() { interface ContactProps {
dict: {
homePage: {
contact: {
slogan: string;
description: string;
button: string;
placeholder: string;
};
};
};
}
export default function Contact({ dict }: ContactProps) {
return ( return (
<div <div
className="mt-10 flex w-full max-w-[90%] flex-col gap-[30px] pt-[120px]" className="mt-10 flex w-full max-w-[90%] flex-col gap-[30px] pt-[120px]"
id="contact" id="contact"
> >
<h2 className="text-center text-[35px] font-[800] text-[#000] capitalize"> <h2 className="text-center text-[35px] font-[800] capitalize text-[#000]">
{siteTexts.en.homePage.contact.slogan} {dict.homePage.contact.slogan}
</h2> </h2>
<p className="text-center text-[18px] font-[600] text-[#000]"> <p className="text-center text-[18px] font-[600] text-[#000]">
{siteTexts.en.homePage.contact.description} {dict.homePage.contact.description}
</p> </p>
<div className="flex w-full flex-wrap items-center justify-center gap-[25px] my-5"> <div className="my-5 flex w-full flex-wrap items-center justify-center gap-[25px]">
<input <input
type="text" type="text"
className="min-w-[340px] rounded-lg border border-[#AFAFAF] bg-[#F8F8F8] p-[22px] text-[18px] font-[400] text-[#797979]" className="min-w-[340px] rounded-lg border border-[#AFAFAF] bg-[#F8F8F8] p-[22px] text-[18px] font-[400] text-[#797979]"
placeholder={siteTexts.en.homePage.contact.placeholder} placeholder={dict.homePage.contact.placeholder}
name="email" name="email"
id="email" id="email"
/> />
<button <button
type="button" type="button"
className="whitespace-nowrap rounded-lg bg-[#FD6F00] px-[35px] py-[20px] text-[20px] font-[600] text-[#fff] capitalize" className="whitespace-nowrap rounded-lg bg-[#FD6F00] px-[35px] py-[20px] text-[20px] font-[600] capitalize text-[#fff]"
> >
{siteTexts.en.homePage.contact.button} {dict.homePage.contact.button}
</button> </button>
</div> </div>
</div> </div>

View File

@ -1,6 +1,8 @@
import React from "react"; import React from "react";
import BurgerButton from "../burger-button/burgerButton"; import BurgerButton from "../burger-button/burgerButton";
import Link from "next/link"; import Link from "next/link";
import SelectLanguage from "../selectLanguage/selectLanguage";
import { cookies } from "next/headers";
export default function Header() { export default function Header() {
return ( return (
@ -59,6 +61,10 @@ export default function Header() {
> >
Blog Blog
</Link> </Link>
<SelectLanguage
selectedLocale={cookies().get("selectedLocale")}
defaultLocale={cookies().get("defaultLocale")}
/>
</ul> </ul>
<div className="flex gap-1"> <div className="flex gap-1">
<button className="flex items-center justify-center whitespace-nowrap rounded-md bg-[#FD6F00] px-4 py-2 text-[16px] font-[600] text-[#fff] xl:text-[20px]"> <button className="flex items-center justify-center whitespace-nowrap rounded-md bg-[#FD6F00] px-4 py-2 text-[16px] font-[600] text-[#fff] xl:text-[20px]">

View File

@ -1,5 +1,6 @@
"use client"; "use client";
import siteTexts from "@/app/_constants/siteTexts";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import Skeleton from "react-loading-skeleton"; import Skeleton from "react-loading-skeleton";
import "react-loading-skeleton/dist/skeleton.css"; import "react-loading-skeleton/dist/skeleton.css";
@ -18,7 +19,18 @@ type Project = {
updated_at: string; updated_at: string;
}; };
export default function MyProjects() { interface MyProjectsProps {
dict: {
homePage: {
projects: {
title: string;
description: string;
};
};
};
}
export default function MyProjects({ dict }: MyProjectsProps) {
const [categories, setCategories] = useState<Category[]>([]); const [categories, setCategories] = useState<Category[]>([]);
const [projects, setProjects] = useState<Project[]>([]); const [projects, setProjects] = useState<Project[]>([]);
const [selectedCategory, setSelectedCategory] = useState<string>("Newest"); const [selectedCategory, setSelectedCategory] = useState<string>("Newest");
@ -78,9 +90,9 @@ export default function MyProjects() {
className="flex w-full max-w-[90%] flex-wrap items-center justify-center pt-[50px]" className="flex w-full max-w-[90%] flex-wrap items-center justify-center pt-[50px]"
id="projects" id="projects"
> >
<h2 className="my-10 text-[55px] font-[800] text-black capitalize">{siteTexts.en.homePage.projects.title}</h2> <h2 className="my-10 text-[55px] font-[800] text-black capitalize">{dict.homePage.projects.title}</h2>
<p className="text-center text-[21px] font-[400] text-black"> <p className="text-center text-[21px] font-[400] text-black">
{siteTexts.en.homePage.projects.description} {dict.homePage.projects.description}
</p> </p>
<ul className="mb-10 mt-20 flex w-full flex-wrap items-center justify-center gap-5"> <ul className="mb-10 mt-20 flex w-full flex-wrap items-center justify-center gap-5">
{categories.length > 0 ? ( {categories.length > 0 ? (

View File

@ -0,0 +1,62 @@
"use client";
import { setCookie } from "cookies-next";
import { LocaleCookieType } from "@/app/types/types";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
interface SelectLanguageProps {
selectedLocale: LocaleCookieType | undefined;
defaultLocale: LocaleCookieType | undefined;
}
export default function SelectLanguage({
selectedLocale,
defaultLocale,
}: SelectLanguageProps) {
const router = useRouter();
const [isOpen, setisOpen] = useState(false);
return (
<div className="relative flex min-w-[100px] justify-center">
{selectedLocale ? (
<p
onClick={() => setisOpen(!isOpen)}
className="mx-2 cursor-pointer select-none whitespace-nowrap rounded-lg px-2 py-[10px] text-[18px] font-[600] hover:text-[#FD6F00]"
>
{selectedLocale?.value}
</p>
) : (
<p
onClick={() => setisOpen(!isOpen)}
className="mx-2 cursor-pointer select-none whitespace-nowrap rounded-lg px-2 py-[10px] text-[18px] font-[600] hover:text-[#FD6F00]"
>
{defaultLocale?.value}
</p>
)}
<ul
className={`absolute start-0 top-10 flex w-full flex-col items-center gap-2 rounded-lg border py-3 ${isOpen ? "scale-100" : "scale-0"}`}
>
<li
className="w-[70%] border-b-2 border-[rgba(0,0,0,0)] pb-1 text-center hover:cursor-pointer hover:border-[#FD6F00]"
onClick={() => {
setCookie("selectedLocale", "fa"), router.refresh();
setisOpen(false);
}}
>
فارسی
</li>
<li
className="w-[70%] border-b-2 border-[rgba(0,0,0,0)] pb-1 text-center hover:cursor-pointer hover:border-[#FD6F00]"
onClick={() => {
setCookie("selectedLocale", "en"), router.refresh();
setisOpen(false);
}}
>
English
</li>
</ul>
</div>
);
}

View File

@ -1,6 +1,5 @@
"use client"; "use client";
import siteTexts from "@/app/_constants/siteTexts";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Skeleton from "react-loading-skeleton"; import Skeleton from "react-loading-skeleton";
import "react-loading-skeleton/dist/skeleton.css"; import "react-loading-skeleton/dist/skeleton.css";
@ -13,7 +12,18 @@ type Service = {
altText: string; altText: string;
}; };
export default function Services() { interface ServicesProps {
dict: {
homePage: {
services: {
title: string;
description: string;
};
};
};
}
export default function Services({ dict }: ServicesProps) {
const [services, setServices] = useState<Service[]>([]); const [services, setServices] = useState<Service[]>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
@ -83,10 +93,10 @@ export default function Services() {
id="services" id="services"
> >
<h2 className="mb-10 text-[55px] font-[800] capitalize text-black"> <h2 className="mb-10 text-[55px] font-[800] capitalize text-black">
{siteTexts.en.homePage.services.title} {dict.homePage.services.title}
</h2> </h2>
<p className="text-center text-[21px] font-[400] text-black"> <p className="text-center text-[21px] font-[400] text-black">
{siteTexts.en.homePage.services.description} {dict.homePage.services.description}
</p> </p>
<div className="service mt-20 flex w-full flex-wrap items-start justify-between gap-8"> <div className="service mt-20 flex w-full flex-wrap items-start justify-between gap-8">
{loading {loading

View File

@ -1,5 +1,5 @@
"use client"; "use client";
import siteTexts from "@/app/_constants/siteTexts";
import React, { useEffect, useRef, useState } from "react"; import React, { useEffect, useRef, useState } from "react";
interface Testimonial { interface Testimonial {
@ -9,7 +9,18 @@ interface Testimonial {
quote: string; quote: string;
} }
export default function Testimonials() { interface TestimonialsProps {
dict: {
homePage: {
testimonials: {
title: string;
description: string;
};
};
};
}
export default function Testimonials({ dict }: TestimonialsProps) {
const [currentIndex, setCurrentIndex] = useState<number>(0); // شروع با آیتم وسطی const [currentIndex, setCurrentIndex] = useState<number>(0); // شروع با آیتم وسطی
const sliderRef = useRef<HTMLDivElement | null>(null); const sliderRef = useRef<HTMLDivElement | null>(null);
const [isHovered, setIsHovered] = useState<boolean>(false); const [isHovered, setIsHovered] = useState<boolean>(false);
@ -85,10 +96,10 @@ export default function Testimonials() {
id="testimonials" id="testimonials"
> >
<h2 className="text-[55px] font-[800] capitalize text-black"> <h2 className="text-[55px] font-[800] capitalize text-black">
{siteTexts.en.homePage.testimonials.title} {dict.homePage.testimonials.title}
</h2> </h2>
<p className="my-10 text-center text-[21px] font-[400] text-black"> <p className="my-10 text-center text-[21px] font-[400] text-black">
{siteTexts.en.homePage.testimonials.description} {dict.homePage.testimonials.description}
</p> </p>
<div <div
className="no-scrollbar relative flex min-h-[780px] w-full select-none flex-nowrap items-start gap-8 overflow-x-scroll py-4 sm:min-h-[560px] lg:min-h-[450px] xl:min-h-[370px] 2xl:min-h-[370px]" // Hide scrollbar using CSS className="no-scrollbar relative flex min-h-[780px] w-full select-none flex-nowrap items-start gap-8 overflow-x-scroll py-4 sm:min-h-[560px] lg:min-h-[450px] xl:min-h-[370px] 2xl:min-h-[370px]" // Hide scrollbar using CSS

View File

@ -4,10 +4,11 @@ import Contact from "./components/contact/contact";
import AboutMe from "./components/about-me/aboutMe"; import AboutMe from "./components/about-me/aboutMe";
import Services from "./components/services/services"; import Services from "./components/services/services";
import MyProjects from "./components/projects/projects"; import MyProjects from "./components/projects/projects";
import siteTexts from "@/app/_constants/siteTexts"; import { getDictionary } from "@/app/lib/functions/dictionaries";
import { getDefaultLocale } from "@/app/lib/functions/utils";
export default async function Home() { export default async function Home() {
const pageText = siteTexts.en; const dict = await getDictionary(getDefaultLocale());
return ( return (
<div className="my-15 flex w-full max-w-[1420px] flex-col items-center gap-[50px]"> <div className="my-15 flex w-full max-w-[1420px] flex-col items-center gap-[50px]">
{/* Home */} {/* Home */}
@ -61,19 +62,19 @@ export default async function Home() {
{/* end */} {/* end */}
<div className="items flex w-full max-w-[90%] flex-col md:max-w-[70%] lg:w-[45%] xl:w-[50%]"> <div className="items flex w-full max-w-[90%] flex-col md:max-w-[70%] lg:w-[45%] xl:w-[50%]">
<p className="text-[20px] font-[400] text-black 2xl:text-[24px]"> <p className="text-[20px] font-[400] text-black 2xl:text-[24px]">
{pageText.homePage.top.greating} {dict.homePage.top.greating}
</p> </p>
<p className="text-[30px] font-[600] text-[#FD6F00] 2xl:text-[32px]"> <p className="text-[30px] font-[600] text-[#FD6F00] 2xl:text-[32px]">
{pageText.homePage.top.name} {dict.homePage.top.name}
</p> </p>
<p className="whitespace-nowrap text-start text-[30px] font-[700] text-black sm:text-[50px] lg:text-[45px] xl:text-[60px] 2xl:text-[80px]"> <p className="whitespace-nowrap text-start text-[30px] font-[700] text-black sm:text-[50px] lg:text-[45px] xl:text-[60px] 2xl:text-[80px]">
{pageText.homePage.top.profession} {dict.homePage.top.profession}
</p> </p>
<p className="mt-[-20px] text-end text-[50px] font-[700] text-black lg:mt-[-30px] lg:text-[50px] xl:text-[70px] 2xl:text-[100px]"> <p className="mt-[-20px] text-end text-[50px] font-[700] text-black lg:mt-[-30px] lg:text-[50px] xl:text-[70px] 2xl:text-[100px]">
{pageText.homePage.top.roll} {dict.homePage.top.roll}
</p> </p>
<p className="my-[40px] text-justify text-[21px] font-[400] text-black lg:pe-0"> <p className="my-[40px] text-justify text-[21px] font-[400] text-black lg:pe-0">
{pageText.homePage.top.descriptionAboutMe} {dict.homePage.top.descriptionAboutMe}
</p> </p>
<button className="flex h-[52px] w-[188px] items-center whitespace-nowrap rounded-md bg-[#FD6F00] p-[18px] px-[52px] text-[21px] font-[400] text-[#fff]"> <button className="flex h-[52px] w-[188px] items-center whitespace-nowrap rounded-md bg-[#FD6F00] p-[18px] px-[52px] text-[21px] font-[400] text-[#fff]">
Hire Me Hire Me
@ -83,13 +84,13 @@ export default async function Home() {
<AboutMe /> <AboutMe />
<Services /> <Services dict={dict} />
<MyProjects /> <MyProjects dict={dict} />
<Testimonials /> <Testimonials dict={dict} />
<Contact /> <Contact dict={dict} />
</div> </div>
); );
} }

View File

@ -1,8 +1,9 @@
import localFont from "next/font/local"; import localFont from "next/font/local";
import "./globals.css"; import "./globals.css";
import { getDefaultLocale } from "./lib/functions/utils";
const mikhak = localFont({ const mikhak = localFont({
src: "fonts/mikhak-v32/variable/Mikhak[DSTY,KSHD,wght].woff2", src: "lib/fonts/mikhak-v32/variable/Mikhak[DSTY,KSHD,wght].woff2",
variable: "--font-mikhak", variable: "--font-mikhak",
weight: "100 900", weight: "100 900",
}); });
@ -12,8 +13,9 @@ export default function RootLayout({
}: Readonly<{ }: Readonly<{
children: React.ReactNode; children: React.ReactNode;
}>) { }>) {
return ( return (
<html lang="en"> <html lang={getDefaultLocale()} dir={getDefaultLocale() === "fa" ? "rtl" : "ltr"}>
<body <body
suppressHydrationWarning={true} suppressHydrationWarning={true}
className={`${mikhak.variable} relative flex h-full w-full flex-col items-center justify-between antialiased`} className={`${mikhak.variable} relative flex h-full w-full flex-col items-center justify-between antialiased`}

View File

@ -0,0 +1,29 @@
{
"homePage": {
"top": {
"greating": "Hi I am",
"name": "parsa aghayi",
"profession": "Frontend & Backend",
"roll": "Developer",
"descriptionAboutMe": "My expertise lies in working with frameworks, focusing on optimizing performance and developing scalable web applications using modern technologies."
},
"services": {
"title": "services",
"description": "In today's digital landscape, having a robust online presence is essential for businesses and individuals alike. Our services cater to a wide range of web development needs, ensuring that your project is functional, visually appealing, and user-friendly."
},
"projects": {
"title": "my projects",
"description": "Explore a selection of my web development projects that showcase my ability to deliver high-quality solutions and create engaging digital experiences. See how I can help bring your vision to life!"
},
"testimonials": {
"title": "testimonials",
"description": "The Testimonials section features feedback from clients and collaborators, highlighting the quality of my services and commitment to client satisfaction. These authentic reviews reflect my professionalism and the positive impact of my work, giving potential clients confidence in choosing me for their web development needs."
},
"contact": {
"slogan": "Lets Design Together",
"description": "The Contact Me section offers a simple way to reach out for inquiries, project proposals, or collaboration opportunities. Fill out the contact form or use the provided information, and I'll respond promptly to discuss your ideas and how I can help.",
"button": "contact me",
"placeholder": "Enter your Email address here..."
}
}
}

View File

@ -0,0 +1,30 @@
{
"homePage": {
"top": {
"greating": "سلام، من",
"name": "پارسا آقایی هستم",
"profession": "توسعه‌دهنده",
"roll": "فرانت‌اند و بک‌اند",
"descriptionAboutMe": "تخصص من در کار با فریمورک‌ها است و بر بهینه‌سازی عملکرد و توسعه برنامه‌های وب مقیاس‌پذیر با استفاده از تکنولوژی‌های مدرن تمرکز دارم."
},
"services": {
"title": "خدمات",
"description": "در دنیای دیجیتال امروز، داشتن یک حضور آنلاین قوی برای کسب‌وکارها و افراد ضروری است. خدمات ما به طیف وسیعی از نیازهای توسعه وب پاسخ می‌دهد و اطمینان می‌دهد که پروژه شما عملکردی، جذاب بصری و کاربرپسند است."
},
"projects": {
"title": "پروژه‌های من",
"description": "مجموعه‌ای از پروژه‌های توسعه وب من را کاوش کنید که توانایی من در ارائه راه‌حل‌های با کیفیت بالا و ایجاد تجربیات دیجیتال جذاب را به نمایش می‌گذارد. ببینید چگونه می‌توانم به تحقق چشم‌انداز شما کمک کنم!"
},
"testimonials": {
"title": "نظرات",
"description": "بخش نظرات شامل بازخوردهایی از مشتریان و همکاران است که کیفیت خدمات من و تعهد من به رضایت مشتری را برجسته می‌کند. این بررسی‌های معتبر حرفه‌ای بودن من و تأثیر مثبت کار من را منعکس می‌کند و به مشتریان بالقوه اعتماد به نفس می‌دهد که من را برای نیازهای توسعه وب خود انتخاب کنند."
},
"contact": {
"slogan": "بگذارید با هم طراحی کنیم",
"description": "بخش تماس با من راهی ساده برای ارتباط با من در مورد سوالات، پیشنهادات پروژه یا فرصت‌های همکاری است. فرم تماس را پر کنید یا از اطلاعات ارائه شده استفاده کنید و من به سرعت پاسخ می‌دهم تا ایده‌های شما را بررسی کنیم و بگویم چگونه می‌توانم کمک کنم.",
"button": "با من تماس بگیرید",
"placeholder": "ایمیل خود را اینجا وارد کنید..."
}
}
}

View File

Before

Width:  |  Height:  |  Size: 156 KiB

After

Width:  |  Height:  |  Size: 156 KiB

Some files were not shown because too many files have changed in this diff Show More