From f22bb8bd5787ef82882230fb76208c4a0775a5c4 Mon Sep 17 00:00:00 2001 From: AshAnand34 Date: Sat, 12 Jul 2025 23:02:35 -0700 Subject: [PATCH] feat: add internationalization support --- package-lock.json | 111 ++++++++-- package.json | 3 + src/components/Hero.tsx | 12 +- src/components/Navbar/index.tsx | 4 +- src/components/ToolHeader.tsx | 4 +- src/components/ToolLayout.tsx | 31 ++- src/components/examples/ToolExamples.tsx | 6 +- src/components/input/BaseFileInput.tsx | 12 +- src/components/input/InputFooter.tsx | 9 +- src/components/input/NumericInputWithUnit.tsx | 4 +- .../input/ToolMultipleAudioInput.tsx | 13 +- src/components/input/ToolMultiplePdfInput.tsx | 11 +- src/components/input/ToolTextInput.tsx | 10 +- src/components/options/ToolOptions.tsx | 4 +- src/components/result/ResultFooter.tsx | 8 +- src/components/result/ToolFileResult.tsx | 10 +- src/components/result/ToolMultiFileResult.tsx | 27 ++- src/components/result/ToolTextResult.tsx | 10 +- src/i18n/en.json | 187 +++++++++++++++++ src/i18n/index.ts | 45 ++++ src/index.tsx | 1 + src/pages/tools/audio/change-speed/index.tsx | 23 +- src/pages/tools/audio/change-speed/meta.ts | 28 ++- src/pages/tools/audio/extract-audio/index.tsx | 26 ++- src/pages/tools/audio/extract-audio/meta.ts | 7 +- src/pages/tools/audio/i18n/en.json | 28 +++ .../tools/csv/change-csv-separator/meta.ts | 19 +- src/pages/tools/csv/csv-to-json/index.tsx | 38 ++-- .../csv/find-incomplete-csv-records/index.tsx | 60 ++++-- .../csv/find-incomplete-csv-records/meta.ts | 9 +- src/pages/tools/csv/i18n/en.json | 63 ++++++ .../tools/csv/insert-csv-columns/index.tsx | 146 +++++++------ .../tools/image/generic/resize/index.tsx | 53 ++--- src/pages/tools/image/i18n/en.json | 40 ++++ src/pages/tools/json/escape-json/meta.ts | 16 +- src/pages/tools/json/i18n/en.json | 39 ++++ src/pages/tools/json/json-to-xml/meta.ts | 18 +- src/pages/tools/json/minify/index.tsx | 15 +- src/pages/tools/json/minify/meta.ts | 15 +- src/pages/tools/json/prettify/index.tsx | 25 ++- src/pages/tools/json/prettify/meta.ts | 15 +- src/pages/tools/json/stringify/meta.ts | 23 +- src/pages/tools/json/tsv-to-json/meta.ts | 19 +- src/pages/tools/json/validateJson/index.tsx | 30 +-- src/pages/tools/json/validateJson/meta.ts | 17 +- src/pages/tools/list/duplicate/index.tsx | 42 ++-- src/pages/tools/list/duplicate/meta.ts | 13 +- .../tools/list/find-most-popular/meta.ts | 13 +- src/pages/tools/list/find-unique/index.tsx | 51 +++-- src/pages/tools/list/find-unique/meta.ts | 12 +- src/pages/tools/list/group/index.tsx | 50 ++--- src/pages/tools/list/group/meta.ts | 10 +- src/pages/tools/list/i18n/en.json | 115 ++++++++++ src/pages/tools/list/reverse/index.tsx | 29 +-- src/pages/tools/list/reverse/meta.ts | 10 +- src/pages/tools/list/rotate/meta.ts | 11 +- src/pages/tools/list/shuffle/index.tsx | 24 ++- src/pages/tools/list/shuffle/meta.ts | 11 +- src/pages/tools/list/sort/index.tsx | 66 +++--- src/pages/tools/list/sort/meta.ts | 13 +- src/pages/tools/list/truncate/meta.ts | 13 +- src/pages/tools/list/unwrap/meta.ts | 13 +- src/pages/tools/list/wrap/index.tsx | 35 ++-- src/pages/tools/list/wrap/meta.ts | 13 +- .../number/arithmetic-sequence/index.tsx | 30 ++- .../tools/number/arithmetic-sequence/meta.ts | 25 +-- src/pages/tools/number/generate/index.tsx | 21 +- src/pages/tools/number/generate/meta.ts | 17 +- src/pages/tools/number/i18n/en.json | 45 ++++ src/pages/tools/number/sum/index.tsx | 35 ++-- src/pages/tools/number/sum/meta.ts | 17 +- src/pages/tools/pdf/compress-pdf/index.tsx | 59 +++--- src/pages/tools/pdf/compress-pdf/meta.ts | 7 +- src/pages/tools/pdf/i18n/en.json | 72 +++++++ src/pages/tools/pdf/merge-pdf/index.tsx | 13 +- src/pages/tools/pdf/merge-pdf/meta.ts | 7 +- src/pages/tools/pdf/pdf-to-epub/meta.ts | 7 +- src/pages/tools/pdf/protect-pdf/meta.ts | 7 +- src/pages/tools/pdf/rotate-pdf/index.tsx | 75 +++---- src/pages/tools/pdf/split-pdf/index.tsx | 33 ++- src/pages/tools/pdf/split-pdf/meta.ts | 7 +- src/pages/tools/string/base64/index.tsx | 23 +- src/pages/tools/string/base64/meta.ts | 7 +- src/pages/tools/string/censor/meta.ts | 8 +- .../tools/string/create-palindrome/meta.ts | 7 +- .../tools/string/extract-substring/meta.ts | 11 +- src/pages/tools/string/i18n/en.json | 196 ++++++++++++++++++ src/pages/tools/string/join/index.tsx | 25 ++- src/pages/tools/string/join/meta.ts | 17 +- src/pages/tools/string/palindrome/meta.ts | 7 +- src/pages/tools/string/quote/index.tsx | 29 ++- src/pages/tools/string/quote/meta.ts | 13 +- src/pages/tools/string/randomize-case/meta.ts | 13 +- .../string/remove-duplicate-lines/meta.ts | 7 +- src/pages/tools/string/repeat/index.tsx | 27 ++- src/pages/tools/string/repeat/meta.ts | 7 +- src/pages/tools/string/reverse/index.tsx | 29 ++- src/pages/tools/string/reverse/meta.ts | 7 +- src/pages/tools/string/rot13/index.tsx | 17 +- src/pages/tools/string/rotate/index.tsx | 27 ++- src/pages/tools/string/split/index.tsx | 16 +- src/pages/tools/string/split/meta.ts | 18 +- src/pages/tools/string/statistic/index.tsx | 48 +++-- src/pages/tools/string/statistic/meta.ts | 8 +- .../tools/string/text-replacer/index.tsx | 32 +-- src/pages/tools/string/to-morse/index.tsx | 23 +- src/pages/tools/string/to-morse/meta.ts | 7 +- src/pages/tools/string/truncate/index.tsx | 48 +++-- src/pages/tools/string/truncate/meta.ts | 17 +- src/pages/tools/string/uppercase/index.tsx | 15 +- src/pages/tools/string/uppercase/meta.ts | 13 +- .../tools/time/check-leap-years/index.tsx | 7 +- src/pages/tools/time/check-leap-years/meta.ts | 18 +- .../time/convert-days-to-hours/index.tsx | 13 +- .../tools/time/convert-days-to-hours/meta.ts | 19 +- .../tools/time/convert-hours-to-days/meta.ts | 19 +- .../time/convert-seconds-to-time/index.tsx | 13 +- .../time/convert-seconds-to-time/meta.ts | 19 +- .../time/convert-time-to-seconds/index.tsx | 7 +- .../time/convert-time-to-seconds/meta.ts | 19 +- src/pages/tools/time/crontab-guru/meta.ts | 28 +-- src/pages/tools/time/i18n/en.json | 74 +++++++ .../tools/time/time-between-dates/index.tsx | 25 +-- .../tools/time/time-between-dates/meta.ts | 24 +-- .../tools/time/truncate-clock-time/index.tsx | 35 ++-- .../tools/time/truncate-clock-time/meta.ts | 19 +- src/pages/tools/video/change-speed/index.tsx | 25 ++- src/pages/tools/video/change-speed/meta.ts | 17 +- src/pages/tools/video/compress/index.tsx | 18 +- src/pages/tools/video/compress/meta.ts | 7 +- src/pages/tools/video/crop-video/index.tsx | 60 +++--- src/pages/tools/video/crop-video/meta.ts | 20 +- src/pages/tools/video/flip/index.tsx | 12 +- src/pages/tools/video/flip/meta.ts | 17 +- .../tools/video/gif/change-speed/meta.ts | 17 +- src/pages/tools/video/i18n/en.json | 79 +++++++ src/pages/tools/video/loop/index.tsx | 15 +- src/pages/tools/video/loop/meta.ts | 15 +- src/pages/tools/video/rotate/index.tsx | 12 +- src/pages/tools/video/rotate/meta.ts | 15 +- src/pages/tools/video/trim/index.tsx | 12 +- src/pages/tools/video/trim/meta.ts | 15 +- src/pages/tools/video/video-to-gif/meta.ts | 18 +- src/pages/tools/xml/i18n/en.json | 36 ++++ src/pages/tools/xml/xml-beautifier/index.tsx | 18 +- src/pages/tools/xml/xml-beautifier/meta.ts | 15 +- src/pages/tools/xml/xml-validator/index.tsx | 9 +- src/pages/tools/xml/xml-validator/meta.ts | 15 +- src/tools/defineTool.tsx | 9 +- 149 files changed, 2807 insertions(+), 1045 deletions(-) create mode 100644 src/i18n/en.json create mode 100644 src/i18n/index.ts create mode 100644 src/pages/tools/audio/i18n/en.json create mode 100644 src/pages/tools/csv/i18n/en.json create mode 100644 src/pages/tools/image/i18n/en.json create mode 100644 src/pages/tools/json/i18n/en.json create mode 100644 src/pages/tools/list/i18n/en.json create mode 100644 src/pages/tools/number/i18n/en.json create mode 100644 src/pages/tools/pdf/i18n/en.json create mode 100644 src/pages/tools/string/i18n/en.json create mode 100644 src/pages/tools/time/i18n/en.json create mode 100644 src/pages/tools/video/i18n/en.json create mode 100644 src/pages/tools/xml/i18n/en.json diff --git a/package-lock.json b/package-lock.json index 5b698c2..7ce39b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "@types/lodash": "^4.17.5", "@types/morsee": "^1.0.2", "@types/omggif": "^1.0.5", + "@types/react-i18next": "^7.8.3", "browser-image-compression": "^2.0.2", "buffer": "^6.0.3", "color": "^4.2.3", @@ -32,6 +33,7 @@ "dayjs": "^1.11.13", "fast-xml-parser": "^5.2.5", "formik": "^2.4.6", + "i18next": "^25.3.2", "jimp": "^0.22.12", "js-quantities": "^1.8.0", "jszip": "^3.10.1", @@ -51,6 +53,7 @@ "react-dom": "^18.3.1", "react-filerobot-image-editor": "^4.9.1", "react-helmet": "^6.1.0", + "react-i18next": "^15.6.0", "react-image-crop": "^11.0.7", "react-konva": "^18.2.10", "react-router-dom": "^6.23.1", @@ -302,12 +305,10 @@ } }, "node_modules/@babel/runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", - "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", + "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -3395,6 +3396,12 @@ "hoist-non-react-statics": "^3.3.0" } }, + "node_modules/@types/i18next": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/@types/i18next/-/i18next-12.1.0.tgz", + "integrity": "sha512-qLyqTkp3ZKHsSoX8CNVYcTyTkxlm0aRCUpaUVetgkSlSpiNCdWryOgaYwgbO04tJIfLgBXPcy0tJ3Nl/RagllA==", + "license": "MIT" + }, "node_modules/@types/js-quantities": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/@types/js-quantities/-/js-quantities-1.6.6.tgz", @@ -3484,6 +3491,16 @@ "@types/react": "*" } }, + "node_modules/@types/react-i18next": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@types/react-i18next/-/react-i18next-7.8.3.tgz", + "integrity": "sha512-VPopxbHXz/1Sjl+ljXQQchf6FHXaYLaH0a6TH6KnGOQGD4LzNbUVlofK26S30OIYfYibm8r/sAb2KeTst+AwTQ==", + "license": "MIT", + "dependencies": { + "@types/i18next": "*", + "@types/react": "*" + } + }, "node_modules/@types/react-reconciler": { "version": "0.28.9", "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.9.tgz", @@ -6684,6 +6701,15 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "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/human-signals": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", @@ -6707,6 +6733,37 @@ "url": "https://github.com/sponsors/typicode" } }, + "node_modules/i18next": { + "version": "25.3.2", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.3.2.tgz", + "integrity": "sha512-JSnbZDxRVbphc5jiptxr3o2zocy5dEqpVm9qCGdJwRNO+9saUJS0/u4LnM/13C23fUEWxAylPqKU/NpMV/IjqA==", + "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.27.6" + }, + "peerDependencies": { + "typescript": "^5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -9682,6 +9739,32 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" }, + "node_modules/react-i18next": { + "version": "15.6.0", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.6.0.tgz", + "integrity": "sha512-W135dB0rDfiFmbMipC17nOhGdttO5mzH8BivY+2ybsQBbXvxWIwl3cmeH3T9d+YPBSJu/ouyJKFJTtkK7rJofw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.6", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 23.2.3", + "react": ">= 16.8.0", + "typescript": "^5" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, "node_modules/react-image-crop": { "version": "11.0.7", "resolved": "https://registry.npmjs.org/react-image-crop/-/react-image-crop-11.0.7.tgz", @@ -9880,11 +9963,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, "node_modules/regexp.prototype.flags": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", @@ -11347,7 +11425,7 @@ "version": "5.4.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11631,6 +11709,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/wait-on": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.2.0.tgz", diff --git a/package.json b/package.json index 52860e1..db17368 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@types/lodash": "^4.17.5", "@types/morsee": "^1.0.2", "@types/omggif": "^1.0.5", + "@types/react-i18next": "^7.8.3", "browser-image-compression": "^2.0.2", "buffer": "^6.0.3", "color": "^4.2.3", @@ -49,6 +50,7 @@ "dayjs": "^1.11.13", "fast-xml-parser": "^5.2.5", "formik": "^2.4.6", + "i18next": "^25.3.2", "jimp": "^0.22.12", "js-quantities": "^1.8.0", "jszip": "^3.10.1", @@ -68,6 +70,7 @@ "react-dom": "^18.3.1", "react-filerobot-image-editor": "^4.9.1", "react-helmet": "^6.1.0", + "react-i18next": "^15.6.0", "react-image-crop": "^11.0.7", "react-konva": "^18.2.10", "react-router-dom": "^6.23.1", diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx index c1c6487..9e8e267 100644 --- a/src/components/Hero.tsx +++ b/src/components/Hero.tsx @@ -18,6 +18,7 @@ import { useNavigate } from 'react-router-dom'; import _ from 'lodash'; import { Icon } from '@iconify/react'; import { getToolCategoryTitle } from '@utils/string'; +import { useTranslation } from 'react-i18next'; const GroupHeader = styled('div')(({ theme }) => ({ position: 'sticky', @@ -48,6 +49,7 @@ const exampleTools: { label: string; url: string }[] = [ { label: 'Calculate number sum', url: '/number/sum' } ]; export default function Hero() { + const { t } = useTranslation(); const [inputValue, setInputValue] = useState(''); const theme = useTheme(); const [filteredTools, setFilteredTools] = useState(tools); @@ -64,13 +66,13 @@ export default function Hero() { - Get Things Done Quickly with{' '} + {t('hero.title')}{' '} - OmniTools + {t('hero.brand')} @@ -79,9 +81,7 @@ export default function Hero() { fontSize={{ xs: 15, md: 20 }} mb={2} > - Boost your productivity with OmniTools, the ultimate toolkit for getting - things done quickly! Access thousands of user-friendly utilities for - editing images, text, lists, and data, all directly from your browser. + {t('hero.description')} , diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx index 5b1bfcf..39fa477 100644 --- a/src/components/Navbar/index.tsx +++ b/src/components/Navbar/index.tsx @@ -19,6 +19,7 @@ import useMediaQuery from '@mui/material/useMediaQuery'; import { useTheme } from '@mui/material/styles'; import { Icon } from '@iconify/react'; import { Mode } from 'components/App'; +import { useTranslation } from 'react-i18next'; interface NavbarProps { mode: Mode; @@ -29,6 +30,7 @@ const Navbar: React.FC = ({ mode, onChangeMode: onChangeMode }) => { + const { t } = useTranslation(); const navigate = useNavigate(); const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down('md')); @@ -83,7 +85,7 @@ const Navbar: React.FC = ({ /> } > - Buy me a coffee + {t('navbar.buyMeACoffee')} ]; const drawerList = ( diff --git a/src/components/ToolHeader.tsx b/src/components/ToolHeader.tsx index 8d88f13..dfe42f6 100644 --- a/src/components/ToolHeader.tsx +++ b/src/components/ToolHeader.tsx @@ -7,6 +7,7 @@ import { Icon, IconifyIcon } from '@iconify/react'; import { categoriesColors } from '../config/uiConfig'; import { getToolsByCategory } from '@tools/index'; import { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; const StyledButton = styled(Button)(({ theme }) => ({ backgroundColor: 'white', @@ -24,6 +25,7 @@ interface ToolHeaderProps { } function ToolLinks() { + const { t } = useTranslation(); const [examplesVisible, setExamplesVisible] = useState(false); useEffect(() => { @@ -63,7 +65,7 @@ function ToolLinks() { sx={{ backgroundColor: 'background.paper' }} onClick={() => scrollToElement('examples')} > - See Examples + {t('toolHeader.seeExamples')} )} diff --git a/src/components/ToolLayout.tsx b/src/components/ToolLayout.tsx index 3da0837..f481c87 100644 --- a/src/components/ToolLayout.tsx +++ b/src/components/ToolLayout.tsx @@ -7,20 +7,33 @@ import AllTools from './allTools/AllTools'; import { getToolsByCategory } from '@tools/index'; import { capitalizeFirstLetter } from '../utils/string'; import { IconifyIcon } from '@iconify/react'; +import { useTranslation } from 'react-i18next'; export default function ToolLayout({ children, title, description, icon, - type + type, + i18n }: { title: string; description: string; icon?: IconifyIcon | string; type: string; children: ReactNode; + i18n?: { + name: string; + description: string; + shortDescription: string; + }; }) { + const { t } = useTranslation(); + + // Use i18n keys if available, otherwise fall back to provided strings + const toolTitle = i18n ? t(i18n.name) : title; + const toolDescription = i18n ? t(i18n.description) : description; + const otherCategoryTools = getToolsByCategory() .find((category) => category.type === type) @@ -41,22 +54,24 @@ export default function ToolLayout({ sx={{ backgroundColor: 'background.default' }} > - {`${title} - OmniTools`} + {`${toolTitle} - OmniTools`} {children} category.type === type)! - .rawTitle - )} tools`} + title={t('toolLayout.allToolsTitle', { + type: capitalizeFirstLetter( + getToolsByCategory().find((category) => category.type === type)! + .rawTitle + ) + })} toolCards={otherCategoryTools} /> diff --git a/src/components/examples/ToolExamples.tsx b/src/components/examples/ToolExamples.tsx index c1c00c6..1b87f16 100644 --- a/src/components/examples/ToolExamples.tsx +++ b/src/components/examples/ToolExamples.tsx @@ -3,6 +3,7 @@ import ExampleCard, { ExampleCardProps } from './ExampleCard'; import React from 'react'; import { GetGroupsType } from '@components/options/ToolOptions'; import { useFormikContext } from 'formik'; +import { useTranslation } from 'react-i18next'; export type CardExampleType = Omit< ExampleCardProps, @@ -24,6 +25,7 @@ export default function ToolExamples({ getGroups, setInput }: ExampleProps) { + const { t } = useTranslation(); const { setValues } = useFormikContext(); function changeInputResult(newInput: string | undefined, newOptions: T) { @@ -39,10 +41,10 @@ export default function ToolExamples({ - {`${title} Examples`} + {t('toolExamples.title', { title })} - {subtitle ?? 'Click to try!'} + {subtitle ?? t('toolExamples.subtitle')} diff --git a/src/components/input/BaseFileInput.tsx b/src/components/input/BaseFileInput.tsx index ebc3840..6502e6c 100644 --- a/src/components/input/BaseFileInput.tsx +++ b/src/components/input/BaseFileInput.tsx @@ -12,6 +12,7 @@ import { globalInputHeight } from '../../config/uiConfig'; import { CustomSnackBarContext } from '../../contexts/CustomSnackBarContext'; import greyPattern from '@assets/grey-pattern.png'; import { isArray } from 'lodash'; +import { useTranslation } from 'react-i18next'; interface BaseFileInputComponentProps extends BaseFileInputProps { children: (props: { preview: string | undefined }) => ReactNode; @@ -26,6 +27,7 @@ export default function BaseFileInput({ children, type }: BaseFileInputComponentProps) { + const { t } = useTranslation(); const [preview, setPreview] = useState(null); const [isDragging, setIsDragging] = useState(false); const theme = useTheme(); @@ -60,9 +62,9 @@ export default function BaseFileInput({ navigator.clipboard .write([clipboardItem]) - .then(() => showSnackBar('File copied', 'success')) + .then(() => showSnackBar(t('baseFileInput.fileCopied'), 'success')) .catch((err) => { - showSnackBar('Failed to copy: ' + err, 'error'); + showSnackBar(t('baseFileInput.copyFailed', { error: err }), 'error'); }); } }; @@ -190,7 +192,7 @@ export default function BaseFileInput({ variant="h6" align="center" > - Drop your {type} here + {t('baseFileInput.dropFileHere', { type })} ) : ( - Click here to select a {type} from your device, press Ctrl+V to - use a {type} from your clipboard, or drag and drop a file from - desktop + {t('baseFileInput.selectFileDescription', { type })} )} diff --git a/src/components/input/InputFooter.tsx b/src/components/input/InputFooter.tsx index 5760022..3a4afa5 100644 --- a/src/components/input/InputFooter.tsx +++ b/src/components/input/InputFooter.tsx @@ -3,6 +3,7 @@ import Button from '@mui/material/Button'; import PublishIcon from '@mui/icons-material/Publish'; import ContentPasteIcon from '@mui/icons-material/ContentPaste'; import ClearIcon from '@mui/icons-material/Clear'; +import { useTranslation } from 'react-i18next'; export default function InputFooter({ handleImport, @@ -13,19 +14,21 @@ export default function InputFooter({ handleCopy?: () => void; handleClear?: () => void; }) { + const { t } = useTranslation(); + return ( {handleCopy && ( )} {handleClear && ( )} diff --git a/src/components/input/NumericInputWithUnit.tsx b/src/components/input/NumericInputWithUnit.tsx index a545685..436d933 100644 --- a/src/components/input/NumericInputWithUnit.tsx +++ b/src/components/input/NumericInputWithUnit.tsx @@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react'; import { Grid, Select, MenuItem } from '@mui/material'; import TextFieldWithDesc from '@components/options/TextFieldWithDesc'; import Qty from 'js-quantities'; +import { useTranslation } from 'react-i18next'; // const siPrefixes: { [key: string]: number } = { @@ -23,6 +24,7 @@ export default function NumericInputWithUnit(props: { onOwnChange?: (value: { value: number; unit: string }) => void; defaultPrefix?: string; }) { + const { t } = useTranslation(); const [inputValue, setInputValue] = useState(props.value.value); const [prefix, setPrefix] = useState(props.defaultPrefix || 'Default prefix'); @@ -158,7 +160,7 @@ export default function NumericInputWithUnit(props: {