diff --git a/.vscode/settings.json b/.vscode/settings.json index d7095c4..bed6884 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,5 @@ { - "cSpell.words": [ - "ایران", - "برای" + "i18n-ally.localesPaths": [ + "public/locales" ] } \ No newline at end of file diff --git a/components/Guide/GuidItem.js b/components/Guide/GuidItem.js index e0928f4..5bf930f 100644 --- a/components/Guide/GuidItem.js +++ b/components/Guide/GuidItem.js @@ -1,3 +1,4 @@ +"use client"; /* eslint-disable react/display-name */ import { Alert, @@ -13,9 +14,9 @@ import { TableCell, Paper, } from "@mui/material"; -import { Box } from "@mui/system"; import React, { useState } from "react"; import ContentCopyIcon from "@mui/icons-material/ContentCopy"; +import { useTranslation } from "next-i18next"; const GuideItem = ({ children }) => { return ( @@ -54,6 +55,8 @@ GuideItem.Description = ({ children }) => { }; GuideItem.Inputs = ({ children }) => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const { t } = useTranslation(); return ( { - نام + {t("queryBuilder:table.expand.guide.table.column.name")} - توضیحات + {t("queryBuilder:table.expand.guide.table.column.description")} - ضروری + {t("queryBuilder:table.expand.guide.table.column.required")} - نوع جواب + {t("queryBuilder:table.expand.guide.table.column.responseType")} @@ -110,6 +113,9 @@ GuideItem.Url = ({ children, domain }) => { setOpen(false); }; + // eslint-disable-next-line react-hooks/rules-of-hooks + const { t } = useTranslation(); + return ( { severity="success" sx={{ width: "100%", fontFamily: "iran-yekan" }} > - کپی شد + {t("queryBuilder:table.expand.guide.table.feedback.copied")} {domain} diff --git a/components/Header/Introduction/IntroductionContent.js b/components/Header/Introduction/IntroductionContent.js index c7a1675..0f9ab81 100644 --- a/components/Header/Introduction/IntroductionContent.js +++ b/components/Header/Introduction/IntroductionContent.js @@ -2,9 +2,11 @@ import React, { useContext } from "react"; import { Box, Grid, Typography } from "@mui/material"; import styles from "./styles.module.css"; import { ThemeContext } from "../../../context/ThemeProvider"; +import { Trans, useTranslation } from "next-i18next"; const Introduction = () => { const { state } = useContext(ThemeContext); + const { t } = useTranslation(); return ( { fontFamily="iran-yekan-bold" fontSize="35px" > - {" "} - وب سرویس - ‌شهر و استان - های ایران + , + }} + /> { fontFamily="iran-yekan" width={["80%", "100"]} > - وب‌سایت وب سرویس شهر و استان‌های ایران API‌ای برای دریافت اطلاعات - شهرها و استان‌ها، شامل نام، کد و موقعیت جغرافیایی، ارائه می‌دهد. + {t("introduction:content.description")} diff --git a/components/Header/IranMap/IranMap.js b/components/Header/IranMap/IranMap.js deleted file mode 100644 index f6dd58b..0000000 --- a/components/Header/IranMap/IranMap.js +++ /dev/null @@ -1,385 +0,0 @@ -import { - Alert, - Box, - Button, - Dialog, - DialogActions, - DialogContent, - DialogTitle, - FormControl, - FormControlLabel, - FormLabel, - Radio, - RadioGroup, - Snackbar, - Tooltip, - Typography, -} from "@mui/material"; -import React, { useState } from "react"; - -export const IranMap = ({ domain }) => { - const [isOpen, setIsOpen] = useState(false); - const [isAlertOpen, setAlertOpen] = useState(false); - const [currentState, setCurrentState] = useState(""); - const [language, setLanguage] = useState("fa"); - const handleClick = (e) => { - const state = e.target.getAttribute("aria-label"); - - setCurrentState(state); - setIsOpen(true); - }; - - const handleCopyStateInformation = () => { - navigator.clipboard.writeText( - `${domain}/api/v1/${language}/states?state=${currentState}` - ); - setAlertOpen(true); - }; - - const handleCopyCitiesOfStateInformation = () => { - navigator.clipboard.writeText( - `${domain}/api/v1/${language}/cities?state=${currentState}` - ); - setAlertOpen(true); - }; - - const handleChangeLanguage = (event) => { - setLanguage(event.target.value); - }; - - return ( - - setAlertOpen(false)} - > - setAlertOpen(false)} - severity="success" - sx={{ - width: "100%", - fontFamily: "iran-yekan", - direction: "ltr", - position: "relative", - }} - > - کپی شد - - - - setIsOpen(false)}> - - دریافت اطلاعات استان {currentState} - - - - - زبان داده های ارسالی - - } label="EN" /> - } label="FA" /> - - - - - - - - - - - - - - - نقشه ایران - - - با استفاده از نقشه ایران میتونید کوئری های خودتون رو بسازید - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -}; diff --git a/components/QueryBuilder/QueryBuilder.js b/components/QueryBuilder/QueryBuilder.js index 1c30282..7f91893 100644 --- a/components/QueryBuilder/QueryBuilder.js +++ b/components/QueryBuilder/QueryBuilder.js @@ -1,3 +1,4 @@ +'use client' import { Box, Collapse, @@ -23,10 +24,13 @@ import { materialLight, } from "react-syntax-highlighter/dist/cjs/styles/prism"; import { ThemeContext } from "../../context/ThemeProvider"; +import { useTranslation } from "next-i18next"; export const QueryBuilder = ({ domain }) => { const { state } = useContext(ThemeContext); + const { t } = useTranslation(); + const [checked, setChecked] = useState(1); const [tab, setTab] = useState(0); @@ -219,7 +223,7 @@ export const QueryBuilder = ({ domain }) => { gutterBottom component="div" > - ورودی ها + {t("queryBuilder:table.expand.inputs")} @@ -229,7 +233,9 @@ export const QueryBuilder = ({ domain }) => { {row.example.parameter.description} - {row.example.parameter.isRequired ? "بله" : "خیر"} + {row.example.parameter.isRequired + ? t("queryBuilder:table.expand.yes") + : t("queryBuilder:table.expand.no")} {row.example.parameter.responseType} @@ -245,7 +251,7 @@ export const QueryBuilder = ({ domain }) => { gutterBottom component="div" > - نمونه جواب + {t("queryBuilder:table.expand.exampleResponse")} { zIndex="999" > setTab(newTab)}> - - + +
@@ -302,10 +308,10 @@ export const QueryBuilder = ({ domain }) => { - عنوان + {t("queryBuilder:table.column.title")} - توضیحات + {t("queryBuilder:table.column.description")} diff --git a/next-i18next.config.js b/next-i18next.config.js new file mode 100644 index 0000000..ecb47ab --- /dev/null +++ b/next-i18next.config.js @@ -0,0 +1,35 @@ +const introduction = require("./public/locales/fa/introduction.json"); +const queryBuilder = require("./public/locales/fa/query-builder.json"); + +/** + * @type {import('next-i18next').UserConfig} + */ +module.exports = { + resources: { + fa: { + introduction, + queryBuilder, + }, + }, + // https://www.i18next.com/overview/configuration-options#logging + debug: process.env.NODE_ENV === "development", + i18n: { + defaultLocale: "fa", + locales: ["fa"], + }, + /** To avoid issues when deploying to some paas (vercel...) */ + localePath: + typeof window === "undefined" + ? require("path").resolve("./public/locales") + : "/locales", + + reloadOnPrerender: process.env.NODE_ENV === "development", + + /** + * @link https://github.com/i18next/next-i18next#6-advanced-configuration + */ + // saveMissing: false, + // strictMode: true, + // serializeConfig: false, + // react: { useSuspense: false } +}; diff --git a/next.config.js b/next.config.js index d19c3b6..d6be906 100644 --- a/next.config.js +++ b/next.config.js @@ -1,7 +1,10 @@ +const { i18n } = require("./next-i18next.config"); + /** @type {import('next').NextConfig} */ const isProd = process.env.NODE_ENV === "production"; const nextConfig = { reactStrictMode: true, + i18n, assetPrefix: isProd ? process.env.WEBSITE_URL : "", }; diff --git a/package.json b/package.json index bcf9dbb..aa09030 100644 --- a/package.json +++ b/package.json @@ -16,11 +16,14 @@ "@mui/material": "^5.6.1", "@mui/styles": "^5.6.2", "axios": "^1.7.2", + "i18next": "^23.16.4", "lodash": "^4.17.21", "next": "^14.2.3", + "next-i18next": "^15.3.1", "nextjs-cors": "^2.1.1", "react": "18.0.0", "react-dom": "18.0.0", + "react-i18next": "^15.1.0", "react-json-view": "^1.21.3", "react-syntax-highlighter": "^15.5.0", "react-tweet-embed": "^2.0.0", diff --git a/pages/_app.js b/pages/_app.js index dedf053..4fd4dff 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -1,13 +1,14 @@ -import ThemeProvider from '../context/ThemeProvider'; -import '../styles/globals.css' - - - +import { appWithTranslation } from "next-i18next"; +import i18nextConfig from "../next-i18next.config"; +import ThemeProvider from "../context/ThemeProvider"; +import "../styles/globals.css"; function MyApp({ Component, pageProps }) { - return - - + return ( + + + + ); } -export default MyApp +export default appWithTranslation(MyApp, i18nextConfig); diff --git a/public/locales/fa/common.json b/public/locales/fa/common.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/public/locales/fa/common.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/public/locales/fa/introduction.json b/public/locales/fa/introduction.json new file mode 100644 index 0000000..2b9c82c --- /dev/null +++ b/public/locales/fa/introduction.json @@ -0,0 +1,6 @@ +{ + "content": { + "title": "وب سرویس شهر و استان های ایران", + "description": "وب‌سایت وب سرویس شهر و استان‌های ایران API‌ای برای دریافت اطلاعات شهرها و استان‌ها، شامل نام، کد و موقعیت جغرافیایی، ارائه می‌دهد." + } +} diff --git a/public/locales/fa/query-builder.json b/public/locales/fa/query-builder.json new file mode 100644 index 0000000..bd176a2 --- /dev/null +++ b/public/locales/fa/query-builder.json @@ -0,0 +1,32 @@ +{ + "tab": { + "state": "استان", + "city": "شهر" + }, + "table": { + "column": { + "title": "عنوان", + "description": "توضیحات" + }, + "expand": { + "exampleResponse": "نمونه جواب", + "yes": "بله", + "no": "خیر", + "inputs": "ورودی ها", + + "guide": { + "table": { + "column":{ + "name": "نام", + "description": "توضیحات", + "required": "اجباری", + "responseType": "نوع جواب" + }, + "feedback": { + "copied": "کپی شد" + } + } + } + } + } +} diff --git a/yarn.lock b/yarn.lock index 2bd62dd..7ad64ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -57,6 +57,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.23.2", "@babel/runtime@^7.25.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1" + integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/types@^7.16.7": version "7.17.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" @@ -580,6 +587,14 @@ dependencies: "@types/unist" "*" +"@types/hoist-non-react-statics@^3.3.4": + version "3.3.5" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz#dab7867ef789d87e2b4b0003c9d65c49cc44a494" + integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" @@ -1024,6 +1039,11 @@ core-js-pure@^3.20.2: resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.21.1.tgz#8c4d1e78839f5f46208de7230cebfb72bc3bdb51" integrity sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ== +core-js@^3: + version "3.39.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.39.0.tgz#57f7647f4d2d030c32a72ea23a0555b2eaa30f83" + integrity sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g== + cors@^2.8.5: version "2.8.5" resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" @@ -1788,13 +1808,20 @@ highlight.js@^10.4.1, highlight.js@~10.7.0: resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== -hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2: +hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== dependencies: react-is "^16.7.0" +html-parse-stringify@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2" + integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg== + dependencies: + void-elements "3.1.0" + human-signals@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" @@ -1810,6 +1837,18 @@ hyphenate-style-name@^1.0.3: resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d" integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ== +i18next-fs-backend@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/i18next-fs-backend/-/i18next-fs-backend-2.3.2.tgz#580b91c9a306b452112e0a1ad3b07e9fd266e567" + integrity sha512-LIwUlkqDZnUI8lnUxBnEj8K/FrHQTT/Sc+1rvDm9E8YvvY5YxzoEAASNx+W5M9DfD5s77lI5vSAFWeTp26B/3Q== + +i18next@^23.16.4: + version "23.16.4" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-23.16.4.tgz#79c07544f6d6fa803fe8427108d547542c1d6cf4" + integrity sha512-9NIYBVy9cs4wIqzurf7nLXPyf3R78xYbxExVqHLK9od3038rjpyOEzW+XB130kZ1N4PZ9inTtJ471CRJ4Ituyg== + dependencies: + "@babel/runtime" "^7.23.2" + ignore@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" @@ -2350,6 +2389,17 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +next-i18next@^15.3.1: + version "15.3.1" + resolved "https://registry.yarnpkg.com/next-i18next/-/next-i18next-15.3.1.tgz#7603d1194f2eb518372e636b93b5ffa3c809b15d" + integrity sha512-+pa2pZJb7B6k5PKW3TLVMmAodqkNaOBWVYlpWX56mgcEJz0UMW+MKSdKM9Z72CHp6Bp48g7OWwDnLqxXNp/84w== + dependencies: + "@babel/runtime" "^7.23.2" + "@types/hoist-non-react-statics" "^3.3.4" + core-js "^3" + hoist-non-react-statics "^3.3.2" + i18next-fs-backend "^2.3.2" + next@^14.2.3: version "14.2.3" resolved "https://registry.yarnpkg.com/next/-/next-14.2.3.tgz#f117dd5d5f20c307e7b8e4f9c1c97d961008925d" @@ -2672,6 +2722,14 @@ react-dom@18.0.0: loose-envify "^1.1.0" scheduler "^0.21.0" +react-i18next@^15.1.0: + version "15.1.0" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-15.1.0.tgz#9494e4add2389f04c205dd7628c1aa75747b98a3" + integrity sha512-zj3nJynMnZsy2gPZiOTC7XctCY5eQGqT3tcKMmfJWC9FMvgd+960w/adq61j8iPzpwmsXejqID9qC3Mqu1Xu2Q== + dependencies: + "@babel/runtime" "^7.25.0" + html-parse-stringify "^3.0.1" + react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -2760,6 +2818,11 @@ regenerator-runtime@^0.13.4: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + regexp.prototype.flags@^1.4.1: version "1.4.2" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.2.tgz#bf635117a2f4b755595ebb0c0ee2d2a49b2084db" @@ -3148,6 +3211,11 @@ vary@^1: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= +void-elements@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" + integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"