fix: i18n

This commit is contained in:
Ibrahima G. Coulibaly
2025-07-15 18:30:02 +01:00
parent 5ba8955890
commit bf2668b963
12 changed files with 106 additions and 89 deletions

56
.idea/workspace.xml generated
View File

@@ -4,9 +4,19 @@
<option name="autoReloadType" value="SELECTIVE" /> <option name="autoReloadType" value="SELECTIVE" />
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="fix: create-tool.mjs to use i18n object"> <list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="chore: sync locales">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/public/locales/en/translation.json" beforeDir="false" afterPath="$PROJECT_DIR$/public/locales/en/translation.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/public/locales/fr/translation.json" beforeDir="false" afterPath="$PROJECT_DIR$/public/locales/fr/translation.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/public/locales/hi/translation.json" beforeDir="false" afterPath="$PROJECT_DIR$/public/locales/hi/translation.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/Hero.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/Hero.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/ToolHeader.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/ToolHeader.tsx" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/components/ToolHeader.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/ToolHeader.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/ToolLayout.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/ToolLayout.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/i18n/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/i18n/index.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/home/Categories.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/home/Categories.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools-by-category/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools-by-category/index.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/tools/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/tools/index.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/utils/string.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/utils/string.ts" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -338,7 +348,7 @@
<recent name="C:\Users\Ibrahima\IdeaProjects\omni-tools\src\pages\categories" /> <recent name="C:\Users\Ibrahima\IdeaProjects\omni-tools\src\pages\categories" />
</key> </key>
</component> </component>
<component name="RunManager" selected="npm.i18n:pull"> <component name="RunManager" selected="npm.dev">
<configuration name="generatePassword" type="JavaScriptTestRunnerVitest" temporary="true" nameIsGenerated="true"> <configuration name="generatePassword" type="JavaScriptTestRunnerVitest" temporary="true" nameIsGenerated="true">
<node-interpreter value="project" /> <node-interpreter value="project" />
<vitest-package value="$PROJECT_DIR$/node_modules/vitest" /> <vitest-package value="$PROJECT_DIR$/node_modules/vitest" />
@@ -412,9 +422,9 @@
</list> </list>
<recent_temporary> <recent_temporary>
<list> <list>
<item itemvalue="Vitest.generatePassword" />
<item itemvalue="npm.dev" /> <item itemvalue="npm.dev" />
<item itemvalue="npm.i18n:sync" /> <item itemvalue="npm.i18n:sync" />
<item itemvalue="Vitest.generatePassword" />
<item itemvalue="npm.i18n:push" /> <item itemvalue="npm.i18n:push" />
<item itemvalue="npm.i18n:pull" /> <item itemvalue="npm.i18n:pull" />
</list> </list>
@@ -531,22 +541,6 @@
<workItem from="1752493585622" duration="11629000" /> <workItem from="1752493585622" duration="11629000" />
<workItem from="1752507105323" duration="9008000" /> <workItem from="1752507105323" duration="9008000" />
</task> </task>
<task id="LOCAL-00187" summary="fix: tests">
<option name="closed" value="true" />
<created>1743691399769</created>
<option name="number" value="00187" />
<option name="presentableId" value="LOCAL-00187" />
<option name="project" value="LOCAL" />
<updated>1743691399769</updated>
</task>
<task id="LOCAL-00188" summary="chore: uninstall @jspawn/ghostscript-wasm">
<option name="closed" value="true" />
<created>1743691471368</created>
<option name="number" value="00188" />
<option name="presentableId" value="LOCAL-00188" />
<option name="project" value="LOCAL" />
<updated>1743691471368</updated>
</task>
<task id="LOCAL-00189" summary="feat: protect pdf"> <task id="LOCAL-00189" summary="feat: protect pdf">
<option name="closed" value="true" /> <option name="closed" value="true" />
<created>1743705749057</created> <created>1743705749057</created>
@@ -923,7 +917,23 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1752586932190</updated> <updated>1752586932190</updated>
</task> </task>
<option name="localTasksCounter" value="236" /> <task id="LOCAL-00236" summary="fix: show Use this tool only if medium breakpoint">
<option name="closed" value="true" />
<created>1752591387066</created>
<option name="number" value="00236" />
<option name="presentableId" value="LOCAL-00236" />
<option name="project" value="LOCAL" />
<updated>1752591387066</updated>
</task>
<task id="LOCAL-00237" summary="chore: sync locales">
<option name="closed" value="true" />
<created>1752596436284</created>
<option name="number" value="00237" />
<option name="presentableId" value="LOCAL-00237" />
<option name="project" value="LOCAL" />
<updated>1752596436284</updated>
</task>
<option name="localTasksCounter" value="238" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
@@ -970,8 +980,6 @@
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" /> <option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" />
<option name="CHECK_NEW_TODO" value="false" /> <option name="CHECK_NEW_TODO" value="false" />
<option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" /> <option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" />
<MESSAGE value="chore: png icon" />
<MESSAGE value="fix: remove xml viewer" />
<MESSAGE value="feat: convert to jpg" /> <MESSAGE value="feat: convert to jpg" />
<MESSAGE value="feat: edit image" /> <MESSAGE value="feat: edit image" />
<MESSAGE value="fix: favicons" /> <MESSAGE value="fix: favicons" />
@@ -995,7 +1003,9 @@
<MESSAGE value="fix: translations" /> <MESSAGE value="fix: translations" />
<MESSAGE value="chore: delete unused i18n json files" /> <MESSAGE value="chore: delete unused i18n json files" />
<MESSAGE value="fix: create-tool.mjs to use i18n object" /> <MESSAGE value="fix: create-tool.mjs to use i18n object" />
<option name="LAST_COMMIT_MESSAGE" value="fix: create-tool.mjs to use i18n object" /> <MESSAGE value="fix: show Use this tool only if medium breakpoint" />
<MESSAGE value="chore: sync locales" />
<option name="LAST_COMMIT_MESSAGE" value="chore: sync locales" />
</component> </component>
<component name="VgoProject"> <component name="VgoProject">
<integration-enabled>false</integration-enabled> <integration-enabled>false</integration-enabled>

View File

@@ -220,7 +220,7 @@
"seeExamples": "See Examples" "seeExamples": "See Examples"
}, },
"toolLayout": { "toolLayout": {
"allToolsTitle": "All {{type}} Tools" "allToolsTitle": "All {{type}}"
}, },
"toolMultiFileResult": { "toolMultiFileResult": {
"copied": "File copied", "copied": "File copied",

View File

@@ -58,7 +58,7 @@
"description": "Outils pour travailler avec des images PNG : convertissez des PNG en JPG, créez des PNG transparents, modifiez les couleurs PNG, recadrez, faites pivoter, redimensionnez des PNG et bien plus encore.", "description": "Outils pour travailler avec des images PNG : convertissez des PNG en JPG, créez des PNG transparents, modifiez les couleurs PNG, recadrez, faites pivoter, redimensionnez des PNG et bien plus encore.",
"title": "Outils PNG" "title": "Outils PNG"
}, },
"seeAll": "Tout voir {{title}}", "seeAll": "Voir les {{title}}",
"string": { "string": {
"description": "Outils pour travailler avec du texte : convertissez du texte en images, recherchez et remplacez du texte, divisez du texte en fragments, joignez des lignes de texte, répétez du texte et bien plus encore.", "description": "Outils pour travailler avec du texte : convertissez du texte en images, recherchez et remplacez du texte, divisez du texte en fragments, joignez des lignes de texte, répétez du texte et bien plus encore.",
"title": "Outils de texte" "title": "Outils de texte"
@@ -220,7 +220,7 @@
"seeExamples": "Voir des exemples" "seeExamples": "Voir des exemples"
}, },
"toolLayout": { "toolLayout": {
"allToolsTitle": "Tous {{type}} Outils" "allToolsTitle": "Tous les {{type}}"
}, },
"toolMultiFileResult": { "toolMultiFileResult": {
"copied": "Fichier copié", "copied": "Fichier copié",

View File

@@ -220,7 +220,7 @@
"seeExamples": "उदाहरण देखें" "seeExamples": "उदाहरण देखें"
}, },
"toolLayout": { "toolLayout": {
"allToolsTitle": "सभी {{type}} टूल्स" "allToolsTitle": "सभी {{type}}"
}, },
"toolMultiFileResult": { "toolMultiFileResult": {
"copied": "फ़ाइल कॉपी की गई", "copied": "फ़ाइल कॉपी की गई",

View File

@@ -151,7 +151,7 @@ export default function Hero() {
renderGroup={(params) => { renderGroup={(params) => {
return ( return (
<li key={params.key}> <li key={params.key}>
<GroupHeader>{getToolCategoryTitle(params.group)}</GroupHeader> <GroupHeader>{getToolCategoryTitle(params.group, t)}</GroupHeader>
<GroupItems>{params.children}</GroupItems> <GroupItems>{params.children}</GroupItems>
</li> </li>
); );

View File

@@ -11,6 +11,7 @@ import { isBookmarked, toggleBookmarked } from '@utils/bookmark';
import IconButton from '@mui/material/IconButton'; import IconButton from '@mui/material/IconButton';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import useMediaQuery from '@mui/material/useMediaQuery'; import useMediaQuery from '@mui/material/useMediaQuery';
import { validNamespaces } from '../i18n';
const StyledButton = styled(Button)(({ theme }) => ({ const StyledButton = styled(Button)(({ theme }) => ({
backgroundColor: 'white', backgroundColor: 'white',
@@ -94,6 +95,7 @@ export default function ToolHeader({
path path
}: ToolHeaderProps) { }: ToolHeaderProps) {
const theme = useTheme(); const theme = useTheme();
const { t } = useTranslation();
const [bookmarked, setBookmarked] = useState<boolean>(isBookmarked(path)); const [bookmarked, setBookmarked] = useState<boolean>(isBookmarked(path));
return ( return (
<Box my={4}> <Box my={4}>
@@ -101,7 +103,7 @@ export default function ToolHeader({
items={[ items={[
{ title: 'All tools', link: '/' }, { title: 'All tools', link: '/' },
{ {
title: getToolsByCategory().find( title: getToolsByCategory(t).find(
(category) => category.type === type (category) => category.type === type
)!.rawTitle, )!.rawTitle,
link: '/categories/' + type link: '/categories/' + type

View File

@@ -43,7 +43,7 @@ export default function ToolLayout({
const toolDescription: string = t(i18n.description); const toolDescription: string = t(i18n.description);
const otherCategoryTools = const otherCategoryTools =
getToolsByCategory() getToolsByCategory(t)
.find((category) => category.type === type) .find((category) => category.type === type)
?.tools.filter((tool) => t(tool.name) !== toolTitle) ?.tools.filter((tool) => t(tool.name) !== toolTitle)
.map((tool) => ({ .map((tool) => ({
@@ -75,10 +75,10 @@ export default function ToolLayout({
{children} {children}
<Separator backgroundColor="#5581b5" margin="50px" /> <Separator backgroundColor="#5581b5" margin="50px" />
<AllTools <AllTools
title={t('toolLayout.allToolsTitle', { title={t('translation:toolLayout.allToolsTitle', {
type: capitalizeFirstLetter( type: capitalizeFirstLetter(
getToolsByCategory().find((category) => category.type === type)! getToolsByCategory(t).find((category) => category.type === type)!
.rawTitle .title
) )
})} })}
toolCards={otherCategoryTools} toolCards={otherCategoryTools}

View File

@@ -2,7 +2,7 @@ import i18n, { ParseKeys } from 'i18next';
import { initReactI18next } from 'react-i18next'; import { initReactI18next } from 'react-i18next';
import Backend from 'i18next-http-backend'; import Backend from 'i18next-http-backend';
export const validNamespaces: string[] = [ export const validNamespaces: (string | 'translation')[] = [
'string', 'string',
'number', 'number',
'video', 'video',

View File

@@ -9,6 +9,7 @@ import { categoriesColors } from 'config/uiConfig';
import { Icon } from '@iconify/react'; import { Icon } from '@iconify/react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { getI18nNamespaceFromToolCategory } from '@utils/string'; import { getI18nNamespaceFromToolCategory } from '@utils/string';
import { validNamespaces } from '../../i18n';
type ArrayElement<ArrayType extends readonly unknown[]> = type ArrayElement<ArrayType extends readonly unknown[]> =
ArrayType extends readonly (infer ElementType)[] ? ElementType : never; ArrayType extends readonly (infer ElementType)[] ? ElementType : never;
@@ -32,10 +33,10 @@ const SingleCategory = function ({
`categories.${category.type}.description`, `categories.${category.type}.description`,
category.description category.description
); );
const seeAllText = t('categories.seeAll', 'See all {{title}}', { const seeAllText = t('translation:categories.seeAll', 'See all {{title}}', {
title: categoryTitle title: categoryTitle
}); });
const tryText = t('categories.try', 'Try {{title}}', { const tryText = t('translation:categories.try', 'Try {{title}}', {
//@ts-ignore //@ts-ignore
title: t(category.example.title) title: t(category.example.title)
}); });
@@ -111,9 +112,10 @@ const SingleCategory = function ({
); );
}; };
export default function Categories() { export default function Categories() {
const { t } = useTranslation();
return ( return (
<Grid width={'80%'} container mt={2} spacing={2}> <Grid width={'80%'} container mt={2} spacing={2}>
{getToolsByCategory().map((category, index) => ( {getToolsByCategory(t).map((category, index) => (
<SingleCategory key={category.type} category={category} index={index} /> <SingleCategory key={category.type} category={category} index={index} />
))} ))}
</Grid> </Grid>

View File

@@ -23,7 +23,7 @@ import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import SearchIcon from '@mui/icons-material/Search'; import SearchIcon from '@mui/icons-material/Search';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { I18nNamespaces } from '../../i18n'; import { I18nNamespaces, validNamespaces } from '../../i18n';
const StyledLink = styled(Link)(({ theme }) => ({ const StyledLink = styled(Link)(({ theme }) => ({
'&:hover': { '&:hover': {
@@ -36,16 +36,12 @@ export default function ToolsByCategory() {
const mainContentRef = React.useRef<HTMLDivElement>(null); const mainContentRef = React.useRef<HTMLDivElement>(null);
const { categoryName } = useParams(); const { categoryName } = useParams();
const [searchTerm, setSearchTerm] = React.useState<string>(''); const [searchTerm, setSearchTerm] = React.useState<string>('');
const rawTitle = getToolCategoryTitle(categoryName as string); const { t } = useTranslation(validNamespaces);
const rawTitle = getToolCategoryTitle(categoryName as string, t);
// First get tools by category without filtering // First get tools by category without filtering
const toolsByCategory = const toolsByCategory =
getToolsByCategory().find(({ type }) => type === categoryName)?.tools ?? []; getToolsByCategory(t).find(({ type }) => type === categoryName)?.tools ??
[];
const namespace =
toolsByCategory.length > 0
? getI18nNamespaceFromToolCategory(toolsByCategory[0].type)
: 'translation';
const { t } = useTranslation(namespace);
const categoryTools = filterTools(toolsByCategory, searchTerm, t); const categoryTools = filterTools(toolsByCategory, searchTerm, t);
@@ -77,10 +73,9 @@ export default function ToolsByCategory() {
<IconButton onClick={() => navigate('/')}> <IconButton onClick={() => navigate('/')}>
<ArrowBackIcon color={'primary'} /> <ArrowBackIcon color={'primary'} />
</IconButton> </IconButton>
<Typography <Typography fontSize={22} color={theme.palette.primary.main}>
fontSize={22} {t('translation:toolLayout.allToolsTitle', { type: rawTitle })}
color={theme.palette.primary.main} </Typography>
>{`All ${rawTitle} Tools`}</Typography>
</Stack> </Stack>
<TextField <TextField
placeholder={'Search'} placeholder={'Search'}

View File

@@ -14,7 +14,7 @@ import { IconifyIcon } from '@iconify/react';
import { pdfTools } from '../pages/tools/pdf'; import { pdfTools } from '../pages/tools/pdf';
import { xmlTools } from '../pages/tools/xml'; import { xmlTools } from '../pages/tools/xml';
import { TFunction } from 'i18next'; import { TFunction } from 'i18next';
import { I18nNamespaces } from '../i18n'; import { FullI18nKey, I18nNamespaces } from '../i18n';
const toolCategoriesOrder: ToolCategory[] = [ const toolCategoriesOrder: ToolCategory[] = [
'image-generic', 'image-generic',
@@ -47,95 +47,93 @@ export const tools: DefinedTool[] = [
]; ];
const categoriesConfig: { const categoriesConfig: {
type: ToolCategory; type: ToolCategory;
value: string; title: FullI18nKey;
title?: string; value: FullI18nKey;
icon: IconifyIcon | string; icon: IconifyIcon | string;
}[] = [ }[] = [
{ {
type: 'string', type: 'string',
title: 'Text',
icon: 'solar:text-bold-duotone', icon: 'solar:text-bold-duotone',
value: value: 'translation:categories.string.description',
'Tools for working with text convert text to images, find and replace text, split text into fragments, join text lines, repeat text, and much more.' title: 'translation:categories.string.title'
}, },
{ {
type: 'png', type: 'png',
icon: 'ph:file-png-thin', icon: 'ph:file-png-thin',
value: value: 'translation:categories.png.description',
'Tools for working with PNG images convert PNGs to JPGs, create transparent PNGs, change PNG colors, crop, rotate, resize PNGs, and much more.' title: 'translation:categories.png.title'
}, },
{ {
type: 'number', type: 'number',
icon: 'lsicon:number-filled', icon: 'lsicon:number-filled',
value: value: 'translation:categories.number.description',
'Tools for working with numbers generate number sequences, convert numbers to words and words to numbers, sort, round, factor numbers, and much more.' title: 'translation:categories.number.title'
}, },
{ {
type: 'gif', type: 'gif',
icon: 'material-symbols-light:gif-rounded', icon: 'material-symbols-light:gif-rounded',
value: value: 'translation:categories.gif.description',
'Tools for working with GIF animations create transparent GIFs, extract GIF frames, add text to GIF, crop, rotate, reverse GIFs, and much more.' title: 'translation:categories.gif.title'
}, },
{ {
type: 'list', type: 'list',
icon: 'solar:list-bold-duotone', icon: 'solar:list-bold-duotone',
value: value: 'translation:categories.list.description',
'Tools for working with lists sort, reverse, randomize lists, find unique and duplicate list items, change list item separators, and much more.' title: 'translation:categories.list.title'
}, },
{ {
type: 'json', type: 'json',
icon: 'lets-icons:json-light', icon: 'lets-icons:json-light',
value: value: 'translation:categories.json.description',
'Tools for working with JSON data structures prettify and minify JSON objects, flatten JSON arrays, stringify JSON values, analyze data, and much more' title: 'translation:categories.json.title'
}, },
{ {
type: 'time', type: 'time',
icon: 'mdi:clock-time-five', icon: 'mdi:clock-time-five',
value: value: 'translation:categories.time.description',
'Tools for working with time and date calculate time differences, convert between time zones, format dates, generate date sequences, and much more.' title: 'translation:categories.time.title'
}, },
{ {
type: 'csv', type: 'csv',
icon: 'material-symbols-light:csv-outline', icon: 'material-symbols-light:csv-outline',
value: value: 'translation:categories.csv.description',
'Tools for working with CSV files - convert CSV to different formats, manipulate CSV data, validate CSV structure, and process CSV files efficiently.' title: 'translation:categories.csv.title'
}, },
{ {
type: 'video', type: 'video',
icon: 'lets-icons:video-light', icon: 'lets-icons:video-light',
value: value: 'translation:categories.video.description',
'Tools for working with videos extract frames from videos, create GIFs from videos, convert videos to different formats, and much more.' title: 'translation:categories.video.title'
}, },
{ {
type: 'pdf', type: 'pdf',
icon: 'tabler:pdf', icon: 'tabler:pdf',
value: value: 'translation:categories.pdf.description',
'Tools for working with PDF files - extract text from PDFs, convert PDFs to other formats, manipulate PDFs, and much more.' title: 'translation:categories.pdf.title'
}, },
{ {
type: 'time', type: 'time',
icon: 'fluent-mdl2:date-time', icon: 'fluent-mdl2:date-time',
value: value: 'translation:categories.time.description',
'Tools for working with time and date draw clocks and calendars, generate time and date sequences, calculate average time, convert between time zones, and much more.' title: 'translation:categories.time.title'
}, },
{ {
type: 'image-generic', type: 'image-generic',
title: 'Image',
icon: 'material-symbols-light:image-outline-rounded', icon: 'material-symbols-light:image-outline-rounded',
value: value: 'translation:categories.image-generic.description',
'Tools for working with pictures compress, resize, crop, convert to JPG, rotate, remove background and much more.' title: 'translation:categories.image-generic.title'
}, },
{ {
type: 'audio', type: 'audio',
icon: 'ic:twotone-audiotrack', icon: 'ic:twotone-audiotrack',
value: value: 'translation:categories.audio.description',
'Tools for working with audio extract audio from video, adjusting audio speed, merging multiple audio files and much more.' title: 'translation:categories.audio.title'
}, },
{ {
type: 'xml', type: 'xml',
icon: 'mdi-light:xml', icon: 'mdi-light:xml',
value: value: 'translation:categories.xml.description',
'Tools for working with XML data structures - viewer, beautifier, validator and much more' title: 'translation:categories.xml.title'
} }
]; ];
// use for changelogs // use for changelogs
@@ -162,7 +160,9 @@ export const filterTools = (
); );
}; };
export const getToolsByCategory = (): { export const getToolsByCategory = (
t: TFunction<I18nNamespaces[]>
): {
title: string; title: string;
rawTitle: string; rawTitle: string;
description: string; description: string;
@@ -179,9 +179,13 @@ export const getToolsByCategory = (): {
(config) => config.type === type (config) => config.type === type
); );
return { return {
rawTitle: categoryConfig?.title ?? capitalizeFirstLetter(type), rawTitle: categoryConfig?.title
title: `${categoryConfig?.title ?? capitalizeFirstLetter(type)} Tools`, ? t(categoryConfig.title)
description: categoryConfig?.value ?? '', : capitalizeFirstLetter(type),
title: categoryConfig?.title
? t(categoryConfig.title)
: `${capitalizeFirstLetter(type)} Tools`,
description: categoryConfig?.value ? t(categoryConfig.value) : '',
type, type,
icon: categoryConfig!.icon, icon: categoryConfig!.icon,
tools: tools ?? [], tools: tools ?? [],

View File

@@ -2,6 +2,7 @@ import { UpdateField } from '@components/options/ToolOptions';
import { getToolsByCategory } from '@tools/index'; import { getToolsByCategory } from '@tools/index';
import { ToolCategory } from '@tools/defineTool'; import { ToolCategory } from '@tools/defineTool';
import { I18nNamespaces, validNamespaces } from '../i18n'; import { I18nNamespaces, validNamespaces } from '../i18n';
import { TFunction } from 'i18next';
// Here starting the shared values for string manipulation. // Here starting the shared values for string manipulation.
@@ -109,8 +110,11 @@ export function itemCounter(
return dict; return dict;
} }
export const getToolCategoryTitle = (categoryName: string): string => export const getToolCategoryTitle = (
getToolsByCategory().find((category) => category.type === categoryName)! categoryName: string,
t: TFunction<I18nNamespaces[]>
): string =>
getToolsByCategory(t).find((category) => category.type === categoryName)!
.rawTitle; .rawTitle;
// Type guard to check if a value is a valid I18nNamespaces // Type guard to check if a value is a valid I18nNamespaces