From fffcdaa526a6845fffc1d1f51114cf419203e30f Mon Sep 17 00:00:00 2001 From: SlimusMinus Date: Thu, 9 Jul 2026 00:09:45 +0300 Subject: [PATCH] added english language --- package-lock.json | 474 +++---------------- package.json | 3 + src/components/AuthModal.jsx | 58 +-- src/components/Authentication.jsx | 83 ++-- src/components/CreateUpdatePost.jsx | 53 ++- src/components/DateCalendarPopup.jsx | 11 +- src/components/PostOnFeed.jsx | 20 +- src/components/RightSidebar.jsx | 13 +- src/components/RulesModal.jsx | 26 +- src/components/Sidebar.jsx | 50 +- src/components/landing/FinalCta.jsx | 7 +- src/components/landing/Footer.jsx | 5 +- src/components/landing/Hero.jsx | 13 +- src/components/landing/Navbar.jsx | 27 +- src/components/landing/StepsSection.jsx | 21 +- src/components/profile/ProfileFormFields.jsx | 33 +- src/components/profile/ProfileInfo.jsx | 5 +- src/components/profile/ProfileModal.jsx | 4 +- src/components/profile/profileValidation.js | 10 +- src/config/feedConstants.js | 22 +- src/hooks/useAuth.js | 4 +- src/hooks/useCreatePost.js | 2 +- src/hooks/useDeleteChat.js | 2 +- src/hooks/useImageUpload.js | 6 +- src/hooks/useUpdatePost.js | 2 +- src/hooks/useUpdateProfile.js | 2 +- src/index.js | 1 + src/pages/ChatListPage.jsx | 22 +- src/pages/ChatPage.jsx | 10 +- src/pages/MainFeed.jsx | 64 +-- src/pages/ReviewsPage.jsx | 46 +- src/styles/NakhodkaLanding.css | 26 + src/utils/validatePostForm.js | 16 +- 33 files changed, 454 insertions(+), 687 deletions(-) diff --git a/package-lock.json b/package-lock.json index d8673ef..8879ae4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,12 +22,15 @@ "autoprefixer": "^10.5.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "emoji-mart": "^5.6.0", "framer-motion": "^12.38.0", + "i18next": "^22.5.1", "lucide-react": "^1.11.0", "postcss": "^8.5.12", "react": "^18.3.1", "react-dom": "^18.3.1", "react-hook-form": "^7.74.0", + "react-i18next": "^12.3.1", "react-router-dom": "^7.14.2", "react-scripts": "5.0.1", "sockjs-client": "^1.6.1", @@ -3037,16 +3040,6 @@ "node": ">= 8" } }, - "node_modules/@oxc-project/types": { - "version": "0.127.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.127.0.tgz", - "integrity": "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==", - "license": "MIT", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/Boshen" - } - }, "node_modules/@pbe/react-yandex-maps": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/@pbe/react-yandex-maps/-/react-yandex-maps-1.2.5.tgz", @@ -3110,270 +3103,6 @@ } } }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.17.tgz", - "integrity": "sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.17.tgz", - "integrity": "sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.17.tgz", - "integrity": "sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.17.tgz", - "integrity": "sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.17.tgz", - "integrity": "sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.17.tgz", - "integrity": "sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.17.tgz", - "integrity": "sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.17.tgz", - "integrity": "sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.17.tgz", - "integrity": "sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.17.tgz", - "integrity": "sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.17.tgz", - "integrity": "sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.17.tgz", - "integrity": "sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.17.tgz", - "integrity": "sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.17.tgz", - "integrity": "sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.17.tgz", - "integrity": "sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.17.tgz", - "integrity": "sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==", - "license": "MIT", - "peer": true - }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -7556,8 +7285,7 @@ "version": "5.6.0", "resolved": "https://registry.npmjs.org/emoji-mart/-/emoji-mart-5.6.0.tgz", "integrity": "sha512-eJp3QRe79pjwa+duv+n7+5YsNhRcMl812EcFVwrnRvYKoNPoQb5qxU8DG6Bgwji0akHdp6D4Ln6tYLG58MFSow==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -9695,6 +9423,15 @@ "node": ">=12" } }, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "license": "MIT", + "dependencies": { + "void-elements": "3.1.0" + } + }, "node_modules/html-webpack-plugin": { "version": "5.6.7", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.7.tgz", @@ -9852,6 +9589,29 @@ "node": ">=10.17.0" } }, + "node_modules/i18next": { + "version": "22.5.1", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-22.5.1.tgz", + "integrity": "sha512-8TGPgM3pAD+VRsMtUMNknRz3kzqwp/gPALrWMsDnmC1mKqJwpWyooQRLMcbTwq8z8YwSmuj+ZYvc+xCuEpkssA==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.6" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -14873,6 +14633,28 @@ "react": "^16.8.0 || ^17 || ^18 || ^19" } }, + "node_modules/react-i18next": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.3.1.tgz", + "integrity": "sha512-5v8E2XjZDFzK7K87eSwC7AJcAkcLt5xYZ4+yTPDAW1i7C93oOY1dnr4BaQM7un4Hm+GmghuiPvevWwlca5PwDA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.6", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 19.0.0", + "react": ">= 16.8.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -15468,40 +15250,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rolldown": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.17.tgz", - "integrity": "sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@oxc-project/types": "=0.127.0", - "@rolldown/pluginutils": "1.0.0-rc.17" - }, - "bin": { - "rolldown": "bin/cli.mjs" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.17", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.17", - "@rolldown/binding-darwin-x64": "1.0.0-rc.17", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.17", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.17", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.17", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.17", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.17", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.17", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.17", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.17", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.17", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.17", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17" - } - }, "node_modules/rollup": { "version": "2.80.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.80.0.tgz", @@ -17346,20 +17094,6 @@ "is-typedarray": "^1.0.0" } }, - "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "license": "Apache-2.0", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -17599,95 +17333,13 @@ "node": ">= 0.8" } }, - "node_modules/vite": { - "version": "8.0.10", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz", - "integrity": "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==", + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", "license": "MIT", - "peer": true, - "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.10", - "rolldown": "1.0.0-rc.17", - "tinyglobby": "^0.2.16" - }, - "bin": { - "vite": "bin/vite.js" - }, "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.0", - "esbuild": "^0.27.0 || ^0.28.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "@vitejs/devtools": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">=0.10.0" } }, "node_modules/w3c-hr-time": { diff --git a/package.json b/package.json index 7835568..9434387 100644 --- a/package.json +++ b/package.json @@ -17,12 +17,15 @@ "autoprefixer": "^10.5.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "emoji-mart": "^5.6.0", "framer-motion": "^12.38.0", + "i18next": "^22.5.1", "lucide-react": "^1.11.0", "postcss": "^8.5.12", "react": "^18.3.1", "react-dom": "^18.3.1", "react-hook-form": "^7.74.0", + "react-i18next": "^12.3.1", "react-router-dom": "^7.14.2", "react-scripts": "5.0.1", "sockjs-client": "^1.6.1", diff --git a/src/components/AuthModal.jsx b/src/components/AuthModal.jsx index b538ed3..e05828f 100644 --- a/src/components/AuthModal.jsx +++ b/src/components/AuthModal.jsx @@ -1,4 +1,5 @@ import { useState, useCallback } from "react"; +import { useTranslation } from 'react-i18next'; import CloseIcon from "./icons/CloseIcon"; import { useAuthContext } from "../context/AuthContext"; import { API_URL } from "../config/authConstants"; @@ -8,6 +9,7 @@ import { API_URL } from "../config/authConstants"; * Открывается когда неавторизованный пользователь пытается создать объявление. */ export default function AuthModal({ onClose }) { + const { t } = useTranslation(); const [mode, setMode] = useState("login"); // "login" | "register" const { login: saveAuth } = useAuthContext(); @@ -17,7 +19,7 @@ export default function AuthModal({ onClose }) { {/* Header */}
- {mode === "login" ? "Вход в аккаунт" : "Регистрация"} + {mode === "login" ? t('auth.loginModalTitle') : t('auth.registerModalTitle')}

@@ -130,6 +133,7 @@ function LoginFormInline({ saveAuth, onClose, onSwitch }) { // ─── Register form (inline modal) ────────────────────────────────────────── function RegisterFormInline({ saveAuth, onClose, onSwitch }) { + const { t } = useTranslation(); const [form, setForm] = useState({ name: "", login: "", password: "", password2: "", }); @@ -144,11 +148,11 @@ function RegisterFormInline({ saveAuth, onClose, onSwitch }) { const validate = () => { const e = {}; - if (!form.name.trim()) e.name = "Введите имя"; - if (!form.login.trim()) e.login = "Введите логин"; - else if (form.login.length < 3) e.login = "Минимум 3 символа"; - if (form.password.length < 6) e.password = "Минимум 6 символов"; - if (form.password !== form.password2) e.password2 = "Пароли не совпадают"; + if (!form.name.trim()) e.name = t('auth.validateNameRequired'); + if (!form.login.trim()) e.login = t('auth.validateLoginRequired'); + else if (form.login.length < 3) e.login = t('auth.loginMinChars'); + if (form.password.length < 6) e.password = t('auth.validatePasswordMin'); + if (form.password !== form.password2) e.password2 = t('auth.validatePasswordMatch'); setErrors(e); return !Object.keys(e).length; }; @@ -170,7 +174,7 @@ function RegisterFormInline({ saveAuth, onClose, onSwitch }) { }); const regData = await regRes.json(); - if (!regRes.ok) throw new Error(regData.message || "Ошибка регистрации"); + if (!regRes.ok) throw new Error(regData.message || t('auth.registrationError')); // 2. Автоматический вход после регистрации const loginRes = await fetch(`${API_URL}/api/v1/auth/login`, { @@ -194,11 +198,11 @@ function RegisterFormInline({ saveAuth, onClose, onSwitch }) { return ( <>
- + setField("name", e.target.value)} /> @@ -206,11 +210,11 @@ function RegisterFormInline({ saveAuth, onClose, onSwitch }) {
- + setField("login", e.target.value)} /> @@ -219,22 +223,22 @@ function RegisterFormInline({ saveAuth, onClose, onSwitch }) {
- + setField("password", e.target.value)} /> {errors.password &&

{errors.password}

}
- + setField("password2", e.target.value)} /> @@ -246,7 +250,7 @@ function RegisterFormInline({ saveAuth, onClose, onSwitch }) { @@ -254,7 +258,7 @@ function RegisterFormInline({ saveAuth, onClose, onSwitch }) { textAlign: "center", marginTop: 14, color: "rgba(255,255,255,.45)", fontSize: ".85rem" }}> - Уже есть аккаунт?{" "} + {t('auth.hasAccount')}{" "}

diff --git a/src/components/Authentication.jsx b/src/components/Authentication.jsx index 739041b..c45e981 100644 --- a/src/components/Authentication.jsx +++ b/src/components/Authentication.jsx @@ -1,4 +1,5 @@ import { useState, useCallback } from "react"; +import { useTranslation } from 'react-i18next'; import MagnifierLogo from "./MagnifierLogo"; import { useAuth } from "../hooks/useAuth"; import { useRegister } from "../hooks/useRegister"; @@ -9,14 +10,14 @@ import likeSmile from '../assets/success.png'; import { useCheckLogin } from "../hooks/useRegister"; // ─── helpers ────────────────────────────────────────────────────────────────── -function getStrength(val) { +function getStrength(val, t) { if (!val) return { level: 0, label: "", color: "transparent", cls: "" }; let s = 0; if (val.length >= 6) s++; if (val.length >= 10) s++; if (/[A-Z]/.test(val) || /[0-9]/.test(val)) s++; if (/[^A-Za-z0-9]/.test(val)) s++; - const labels = ["", "Слабый", "Средний", "Хороший", "Отличный"]; + const labels = ["", t('auth.strengthWeak'), t('auth.strengthMedium'), t('auth.strengthGood'), t('auth.strengthExcellent')]; const colors = ["", "#ff6b6b", "#ffd166", "#2de0c8", "#2de0c8"]; const cls = ["", "sw", "sm", "ss", "ss"]; return { level: s, label: labels[s], color: colors[s], cls: cls[s] }; @@ -49,6 +50,7 @@ function StepDots({ current, total }) { // LOGIN FORM // ═══════════════════════════════════════════════════════════════════════════════ function LoginForm({ onSwitchToRegister }) { + const { t } = useTranslation(); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [showPass, setShowPass] = useState(false); @@ -56,14 +58,14 @@ function LoginForm({ onSwitchToRegister }) { return (
-

С возвращением

+

{t('auth.welcomeBack')}

{ e.preventDefault(); login(email, password); }} noValidate>
setEmail(e.target.value)} autoComplete="off" @@ -76,7 +78,7 @@ function LoginForm({ onSwitchToRegister }) { setPassword(e.target.value)} style={{ paddingRight: "44px" }} @@ -90,16 +92,16 @@ function LoginForm({ onSwitchToRegister }) { {error &&

{error}

} - +

- Нет аккаунта?{" "} + {t('auth.noAccount')}{" "}

@@ -108,21 +110,22 @@ function LoginForm({ onSwitchToRegister }) { // ─── step 0+1: name + login + passwords ─────────────────────────────────────── function RegStep01({ data, onChange, onNext }) { + const { t } = useTranslation(); const [show1, setShow1] = useState(false); const [show2, setShow2] = useState(false); const [errors, setErrors] = useState({}); - const strength = getStrength(data.password); + const strength = getStrength(data.password, t); const matched = data.password && data.password === data.password2; const { checking: checkingLogin, exists: loginExists } = useCheckLogin(data.login); const validate = () => { const e = {}; - if (!data.name.trim()) e.name = "Введите имя"; - if (!data.login.trim()) e.login = "Введите логин"; - else if (data.login.length < 3) e.login = "Минимум 3 символа"; - if (data.password.length < 6) e.password = "Минимум 6 символов"; - else if (data.password !== data.password2) e.password2 = "Пароли не совпадают"; + if (!data.name.trim()) e.name = t('auth.validateNameRequired'); + if (!data.login.trim()) e.login = t('auth.validateLoginRequired'); + else if (data.login.length < 3) e.login = t('auth.loginMinChars'); + if (data.password.length < 6) e.password = t('auth.validatePasswordMin'); + else if (data.password !== data.password2) e.password2 = t('auth.validatePasswordMatch'); setErrors(e); return !Object.keys(e).length; @@ -131,11 +134,11 @@ function RegStep01({ data, onChange, onNext }) { return ( <>
- + { onChange("name", e.target.value); @@ -147,12 +150,12 @@ function RegStep01({ data, onChange, onNext }) {
- + { onChange("login", e.target.value); @@ -161,12 +164,12 @@ function RegStep01({ data, onChange, onNext }) { autoComplete="off" /> - {checkingLogin &&

Проверяем...

} + {checkingLogin &&

{t('auth.checking')}

} {!checkingLogin && data.login.length >= 3 && (

- {loginExists ? "✗ Логин занят" : "✓ Логин свободен"} + {loginExists ? t('auth.loginTaken') : t('auth.loginFree')}

)} @@ -174,13 +177,13 @@ function RegStep01({ data, onChange, onNext }) {
- +
{ onChange("password", e.target.value); @@ -211,14 +214,14 @@ function RegStep01({ data, onChange, onNext }) {
- +
{ onChange("password2", e.target.value); @@ -232,7 +235,7 @@ function RegStep01({ data, onChange, onNext }) { {data.password2 && (

- {matched ? "✓ Совпадают" : "✗ Не совпадают"} + {matched ? t('auth.passwordsMatch') : t('auth.passwordsNoMatch')}

)} @@ -240,7 +243,7 @@ function RegStep01({ data, onChange, onNext }) {
); @@ -248,16 +251,17 @@ function RegStep01({ data, onChange, onNext }) { // ─── step 2: confirm ────────────────────────────────────────────────────────── function RegStep2({data, onSubmit, onBack, loading, error}) { + const { t } = useTranslation(); return ( <>
- Имя + {t('auth.nameLabel')} {data.name}
- Логин + {t('auth.loginLabel')} @{data.login}
@@ -266,30 +270,32 @@ function RegStep2({data, onSubmit, onBack, loading, error}) { marginBottom: "10px" }}>{error}

} - + ); } // ─── success ────────────────────────────────────────────────────────────────── function RegSuccess({name, onLogin}) { + const { t } = useTranslation(); return (
success

Добро пожаловать, {name}!

-

Аккаунт создан успешно.

+ className="success-title">{t('auth.welcome', { name })}

+

{t('auth.accountCreated')}

); } function RegisterForm({onSwitchToLogin}) { + const { t } = useTranslation(); const [step, setStep] = useState(0); const [form, setForm] = useState( {name: "", login: "", password: "", password2: ""}); @@ -307,7 +313,7 @@ function RegisterForm({onSwitchToLogin}) { return (
-

Создайте аккаунт

+

{t('auth.createAccount')}

{step === 0 && ( @@ -325,8 +331,8 @@ function RegisterForm({onSwitchToLogin}) { {step === 0 && (

- Уже есть аккаунт?{" "} - + {t('auth.hasAccount')}{" "} +

)}
@@ -337,6 +343,7 @@ function RegisterForm({onSwitchToLogin}) { // AUTH PAGE — единственная страница, переключает login ↔ register // ═══════════════════════════════════════════════════════════════════════════════ export default function Authentication() { + const { t } = useTranslation(); const [mode, setMode] = useState("login"); // "login" | "register" const [animating, setAnimating] = useState(false); @@ -367,7 +374,7 @@ export default function Authentication() {
-

Находка

+

{t('app.name')}

{mode === "login" ? switchTo("register")} /> diff --git a/src/components/CreateUpdatePost.jsx b/src/components/CreateUpdatePost.jsx index 2e8d950..8be4a61 100644 --- a/src/components/CreateUpdatePost.jsx +++ b/src/components/CreateUpdatePost.jsx @@ -1,6 +1,7 @@ import {useCallback, useEffect, useState} from "react"; import CloseIcon from "./icons/CloseIcon"; -import { EMPTY_FORM, CATEGORIES } from "../config/feedConstants"; +import { EMPTY_FORM, CATEGORY_KEYS } from "../config/feedConstants"; +import { useTranslation } from 'react-i18next'; import { usePostMutation } from "../hooks/usePostMutation"; import { useImageUpload, ACCEPT } from "../hooks/useImageUpload"; import { validatePostForm } from "../utils/validatePostForm"; @@ -19,6 +20,7 @@ const MAX_SIZE_MB = 5; export default function CreateUpdatePost({ onClose, onSuccess, initialData }) { // Режим: редактирование (true) или создание нового (false) const isEdit = !!initialData; + const { t } = useTranslation(); // Поля формы; при редактировании заполняем из initialData const [form, setForm] = useState( @@ -88,7 +90,7 @@ export default function CreateUpdatePost({ onClose, onSuccess, initialData }) { } if (coords && !addressRef.current) { - setErrors(prev => ({ ...prev, map: "Подождите, адрес загружается..." })); + setErrors(prev => ({ ...prev, map: t('post.mapLoading') })); return; } @@ -136,7 +138,7 @@ export default function CreateUpdatePost({ onClose, onSuccess, initialData }) { {/* Шапка модалки */}
- {isEdit ? "Редактировать объявление" : "Новое объявление"} + {isEdit ? t('post.editTitle') : t('post.newTitle')}
{/* Название */}
- + set("title", e.target.value)} /> @@ -178,11 +180,11 @@ export default function CreateUpdatePost({ onClose, onSuccess, initialData }) { {/* Описание */}
- +