From 8e1348fb63e8d6b5df1df76337d26b36e13811e7 Mon Sep 17 00:00:00 2001 From: Kshitij <160704796+kshitij-ka@users.noreply.github.com> Date: Sun, 3 May 2026 00:01:14 +0530 Subject: [PATCH] feat: add react-i18next with English and Hindi locale support. - Add i18next + react-i18next + i18next-browser-languagedetector. - EN/HI translation files covering all UI strings across every page and component. - Language switcher button in Navbar; choice persisted to localStorage. - document.documentElement.lang synced to active language in App. - Skip-nav link and #main-content anchor for keyboard accessibility. - aria-describedby on modal dialog; page title and meta description in index.html. - Secure page title set to 'BIS SP-21 Standards.' --- web/client/index.html | 4 +- web/client/package-lock.json | 133 ++++++++++---- web/client/package.json | 3 + web/client/src/App.jsx | 30 ++-- web/client/src/components/Footer.jsx | 36 ++-- web/client/src/components/Navbar.css | 14 ++ web/client/src/components/Navbar.jsx | 50 ++++-- web/client/src/components/StandardCard.jsx | 8 +- web/client/src/components/StandardModal.jsx | 53 +++--- web/client/src/i18n.js | 25 +++ web/client/src/index.css | 25 +++ web/client/src/locales/en/translation.json | 183 ++++++++++++++++++++ web/client/src/locales/hi/translation.json | 183 ++++++++++++++++++++ web/client/src/main.jsx | 1 + web/client/src/pages/About.jsx | 75 +++----- web/client/src/pages/Categories.jsx | 12 +- web/client/src/pages/Home.jsx | 75 ++++---- web/client/src/pages/Recommend.jsx | 77 ++++---- web/client/src/pages/Standards.jsx | 66 +++---- 19 files changed, 781 insertions(+), 272 deletions(-) create mode 100644 web/client/src/i18n.js create mode 100644 web/client/src/locales/en/translation.json create mode 100644 web/client/src/locales/hi/translation.json diff --git a/web/client/index.html b/web/client/index.html index 6e56e6e..644d232 100644 --- a/web/client/index.html +++ b/web/client/index.html @@ -4,9 +4,11 @@ - client + + BIS SP‑21 Standards + Skip to main content
diff --git a/web/client/package-lock.json b/web/client/package-lock.json index 8284d98..74a2983 100644 --- a/web/client/package-lock.json +++ b/web/client/package-lock.json @@ -8,8 +8,11 @@ "name": "client", "version": "0.0.0", "dependencies": { + "i18next": "^26.0.8", + "i18next-browser-languagedetector": "^8.2.1", "react": "^19.2.5", "react-dom": "^19.2.5", + "react-i18next": "^17.0.6", "react-router-dom": "^7.14.2" }, "devDependencies": { @@ -216,6 +219,15 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", @@ -664,9 +676,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -684,9 +693,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -704,9 +710,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -724,9 +727,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -744,9 +744,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -764,9 +761,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1509,6 +1503,52 @@ "hermes-estree": "0.25.1" } }, + "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/i18next": { + "version": "26.0.8", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.0.8.tgz", + "integrity": "sha512-BRzLom0mhDhV9v0QhgUUHWQJuwFmnr1194xEcNLYD6ym8y8s542n4jXUvRLnhNTbh9PmpU6kGZamyuGHQMsGjw==", + "funding": [ + { + "type": "individual", + "url": "https://www.locize.com/i18next" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + }, + { + "type": "individual", + "url": "https://www.locize.com" + } + ], + "license": "MIT", + "peerDependencies": { + "typescript": "^5 || ^6" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.2.1.tgz", + "integrity": "sha512-bZg8+4bdmaOiApD7N7BPT9W8MLZG+nPTOFlLiJiT8uzKXFjhxw4v2ierCXOwB5sFDMtuA5G4kgYZ0AznZxQ/cw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -1780,9 +1820,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1804,9 +1841,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1828,9 +1862,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1852,9 +1883,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -2152,6 +2180,33 @@ "react": "^19.2.5" } }, + "node_modules/react-i18next": { + "version": "17.0.6", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-17.0.6.tgz", + "integrity": "sha512-WzJ6SMKF+GTD7JZZqxSR1AKKmXjaSu39sClUrNlwxS4Tl7a99O+ltFy6yhPMO+wgZuxpQjJ2PZkfrQKmAqrLhw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "html-parse-stringify": "^3.0.1", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "i18next": ">= 26.0.1", + "react": ">= 16.8.0", + "typescript": "^5 || ^6" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, "node_modules/react-router": { "version": "7.14.2", "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.14.2.tgz", @@ -2365,6 +2420,15 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/vite": { "version": "8.0.10", "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz", @@ -2443,6 +2507,15 @@ } } }, + "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", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/web/client/package.json b/web/client/package.json index 2b94c6d..43dfff3 100644 --- a/web/client/package.json +++ b/web/client/package.json @@ -10,8 +10,11 @@ "preview": "vite preview" }, "dependencies": { + "i18next": "^26.0.8", + "i18next-browser-languagedetector": "^8.2.1", "react": "^19.2.5", "react-dom": "^19.2.5", + "react-i18next": "^17.0.6", "react-router-dom": "^7.14.2" }, "devDependencies": { diff --git a/web/client/src/App.jsx b/web/client/src/App.jsx index 67e1407..12674e8 100644 --- a/web/client/src/App.jsx +++ b/web/client/src/App.jsx @@ -1,4 +1,6 @@ +import { useEffect } from "react"; import { Routes, Route, Navigate } from "react-router-dom"; +import { useTranslation } from "react-i18next"; import Navbar from "./components/Navbar"; import Footer from "./components/Footer"; import Home from "./pages/Home"; @@ -7,22 +9,26 @@ import Categories from "./pages/Categories"; import About from "./pages/About"; import Recommend from "./pages/Recommend"; -/** - * Main application router. - * Renders layout with Navbar/Footer and routes to pages. - */ export default function App() { + const { i18n } = useTranslation(); + + useEffect(() => { + document.documentElement.lang = i18n.language; + }, [i18n.language]); + return ( <> - - } /> - } /> - } /> - } /> - } /> - } /> - +
+ + } /> + } /> + } /> + } /> + } /> + } /> + +