mirror of
https://github.com/iib0011/omni-tools.git
synced 2025-09-26 09:29:30 +02:00
fix: missing translations
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { Box, FormControlLabel, Radio, RadioGroup } from '@mui/material';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ToolContent from '@components/ToolContent';
|
||||
import { ToolComponentProps } from '@tools/defineTool';
|
||||
import { GetGroupsType } from '@components/options/ToolOptions';
|
||||
@@ -24,6 +25,7 @@ export default function MergeAudio({
|
||||
title,
|
||||
longDescription
|
||||
}: ToolComponentProps) {
|
||||
const { t } = useTranslation('audio');
|
||||
const [input, setInput] = useState<MultiAudioInput[]>([]);
|
||||
const [result, setResult] = useState<File | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -51,7 +53,7 @@ export default function MergeAudio({
|
||||
updateField
|
||||
}) => [
|
||||
{
|
||||
title: 'Output Format',
|
||||
title: t('mergeAudio.outputFormat'),
|
||||
component: (
|
||||
<Box mt={2}>
|
||||
<RadioGroup
|
||||
@@ -87,16 +89,20 @@ export default function MergeAudio({
|
||||
value={input}
|
||||
onChange={setInput}
|
||||
accept={['audio/*', '.mp3', '.wav', '.aac']}
|
||||
title={'Input Audio Files'}
|
||||
title={t('mergeAudio.inputTitle')}
|
||||
type="audio"
|
||||
/>
|
||||
}
|
||||
resultComponent={
|
||||
loading ? (
|
||||
<ToolFileResult title="Merging Audio" value={null} loading={true} />
|
||||
<ToolFileResult
|
||||
title={t('mergeAudio.mergingAudio')}
|
||||
value={null}
|
||||
loading={true}
|
||||
/>
|
||||
) : (
|
||||
<ToolFileResult
|
||||
title="Merged Audio"
|
||||
title={t('mergeAudio.resultTitle')}
|
||||
value={result}
|
||||
extension={result ? result.name.split('.').pop() : undefined}
|
||||
/>
|
||||
@@ -106,7 +112,10 @@ export default function MergeAudio({
|
||||
getGroups={getGroups}
|
||||
setInput={setInput}
|
||||
compute={compute}
|
||||
toolInfo={{ title: `What is ${title}?`, description: longDescription }}
|
||||
toolInfo={{
|
||||
title: t('mergeAudio.toolInfo.title', { title }),
|
||||
description: longDescription
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { Box, FormControlLabel, Radio, RadioGroup } from '@mui/material';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ToolContent from '@components/ToolContent';
|
||||
import { ToolComponentProps } from '@tools/defineTool';
|
||||
import { GetGroupsType } from '@components/options/ToolOptions';
|
||||
@@ -22,6 +23,7 @@ const formatOptions = [
|
||||
];
|
||||
|
||||
export default function Trim({ title, longDescription }: ToolComponentProps) {
|
||||
const { t } = useTranslation('audio');
|
||||
const [input, setInput] = useState<File | null>(null);
|
||||
const [result, setResult] = useState<File | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -48,28 +50,28 @@ export default function Trim({ title, longDescription }: ToolComponentProps) {
|
||||
updateField
|
||||
}) => [
|
||||
{
|
||||
title: 'Time Settings',
|
||||
title: t('trim.timeSettings'),
|
||||
component: (
|
||||
<Box>
|
||||
<TextFieldWithDesc
|
||||
value={values.startTime}
|
||||
onOwnChange={(val) => updateField('startTime', val)}
|
||||
description="Start time in format HH:MM:SS (e.g., 00:00:30)"
|
||||
label="Start Time"
|
||||
description={t('trim.startTimeDescription')}
|
||||
label={t('trim.startTime')}
|
||||
/>
|
||||
<Box mt={2}>
|
||||
<TextFieldWithDesc
|
||||
value={values.endTime}
|
||||
onOwnChange={(val) => updateField('endTime', val)}
|
||||
description="End time in format HH:MM:SS (e.g., 00:01:30)"
|
||||
label="End Time"
|
||||
description={t('trim.endTimeDescription')}
|
||||
label={t('trim.endTime')}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Output Format',
|
||||
title: t('trim.outputFormat'),
|
||||
component: (
|
||||
<Box mt={2}>
|
||||
<RadioGroup
|
||||
@@ -104,15 +106,19 @@ export default function Trim({ title, longDescription }: ToolComponentProps) {
|
||||
<ToolAudioInput
|
||||
value={input}
|
||||
onChange={setInput}
|
||||
title={'Input Audio'}
|
||||
title={t('trim.inputTitle')}
|
||||
/>
|
||||
}
|
||||
resultComponent={
|
||||
loading ? (
|
||||
<ToolFileResult title="Trimming Audio" value={null} loading={true} />
|
||||
<ToolFileResult
|
||||
title={t('trim.trimmingAudio')}
|
||||
value={null}
|
||||
loading={true}
|
||||
/>
|
||||
) : (
|
||||
<ToolFileResult
|
||||
title="Trimmed Audio"
|
||||
title={t('trim.resultTitle')}
|
||||
value={result}
|
||||
extension={result ? result.name.split('.').pop() : undefined}
|
||||
/>
|
||||
@@ -122,7 +128,10 @@ export default function Trim({ title, longDescription }: ToolComponentProps) {
|
||||
getGroups={getGroups}
|
||||
setInput={setInput}
|
||||
compute={compute}
|
||||
toolInfo={{ title: `What is ${title}?`, description: longDescription }}
|
||||
toolInfo={{
|
||||
title: t('trim.toolInfo.title', { title }),
|
||||
description: longDescription
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@@ -131,8 +131,10 @@ export default function CsvToJson({ title }: ToolComponentProps) {
|
||||
setResult(jsonResult);
|
||||
} catch (error) {
|
||||
setResult(
|
||||
`Error: ${
|
||||
error instanceof Error ? error.message : 'Invalid CSV format'
|
||||
`${t('csvToJson.error')}: ${
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: t('csvToJson.invalidCsvFormat')
|
||||
}`
|
||||
);
|
||||
}
|
||||
@@ -211,9 +213,8 @@ export default function CsvToJson({ title }: ToolComponentProps) {
|
||||
}
|
||||
]}
|
||||
toolInfo={{
|
||||
title: 'What Is a CSV to JSON Converter?',
|
||||
description:
|
||||
'This tool transforms Comma Separated Values (CSV) files to JavaScript Object Notation (JSON) data structures. It supports various CSV formats with customizable delimiters, quote characters, and comment symbols. The converter can treat the first row as headers, skip empty lines, and automatically detect data types like numbers and booleans. The resulting JSON can be used for data migration, backups, or as input for other applications.'
|
||||
title: t('csvToJson.toolInfo.title'),
|
||||
description: t('csvToJson.toolInfo.description')
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import React, { useContext, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { InitialValuesType } from './types';
|
||||
import { compressImage } from './service';
|
||||
import ToolContent from '@components/ToolContent';
|
||||
@@ -17,6 +18,7 @@ const initialValues: InitialValuesType = {
|
||||
};
|
||||
|
||||
export default function CompressImage({ title }: ToolComponentProps) {
|
||||
const { t } = useTranslation('image');
|
||||
const [input, setInput] = useState<File | null>(null);
|
||||
const [result, setResult] = useState<File | null>(null);
|
||||
const [isProcessing, setIsProcessing] = useState<boolean>(false);
|
||||
@@ -37,7 +39,7 @@ export default function CompressImage({ title }: ToolComponentProps) {
|
||||
setResult(compressed);
|
||||
setCompressedSize(compressed.size);
|
||||
} else {
|
||||
showSnackBar('Failed to compress image. Please try again.', 'error');
|
||||
showSnackBar(t('compress.failedToCompress'), 'error');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error in compression:', err);
|
||||
@@ -55,12 +57,12 @@ export default function CompressImage({ title }: ToolComponentProps) {
|
||||
value={input}
|
||||
onChange={setInput}
|
||||
accept={['image/*']}
|
||||
title={'Input image'}
|
||||
title={t('compress.inputTitle')}
|
||||
/>
|
||||
}
|
||||
resultComponent={
|
||||
<ToolFileResult
|
||||
title={'Compressed image'}
|
||||
title={t('compress.resultTitle')}
|
||||
value={result}
|
||||
loading={isProcessing}
|
||||
/>
|
||||
@@ -68,14 +70,14 @@ export default function CompressImage({ title }: ToolComponentProps) {
|
||||
initialValues={initialValues}
|
||||
getGroups={({ values, updateField }) => [
|
||||
{
|
||||
title: 'Compression options',
|
||||
title: t('compress.compressionOptions'),
|
||||
component: (
|
||||
<Box>
|
||||
<TextFieldWithDesc
|
||||
name="maxFileSizeInMB"
|
||||
type="number"
|
||||
inputProps={{ min: 0.1, step: 0.1 }}
|
||||
description="Maximum file size in megabytes"
|
||||
description={t('compress.maxFileSizeDescription')}
|
||||
onOwnChange={(value) =>
|
||||
updateNumberField(value, 'maxFileSizeInMB', updateField)
|
||||
}
|
||||
@@ -85,7 +87,7 @@ export default function CompressImage({ title }: ToolComponentProps) {
|
||||
name="quality"
|
||||
type="number"
|
||||
inputProps={{ min: 10, max: 100, step: 1 }}
|
||||
description="Image quality percentage (lower means smaller file size)"
|
||||
description={t('compress.qualityDescription')}
|
||||
onOwnChange={(value) =>
|
||||
updateNumberField(value, 'quality', updateField)
|
||||
}
|
||||
@@ -95,18 +97,20 @@ export default function CompressImage({ title }: ToolComponentProps) {
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'File sizes',
|
||||
title: t('compress.fileSizes'),
|
||||
component: (
|
||||
<Box>
|
||||
<Box>
|
||||
{originalSize !== null && (
|
||||
<Typography>
|
||||
Original Size: {(originalSize / 1024).toFixed(2)} KB
|
||||
{t('compress.originalSize')}:{' '}
|
||||
{(originalSize / 1024).toFixed(2)} KB
|
||||
</Typography>
|
||||
)}
|
||||
{compressedSize !== null && (
|
||||
<Typography>
|
||||
Compressed Size: {(compressedSize / 1024).toFixed(2)} KB
|
||||
{t('compress.compressedSize')}:{' '}
|
||||
{(compressedSize / 1024).toFixed(2)} KB
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
|
@@ -123,9 +123,9 @@ export default function Duplicate({ title }: ToolComponentProps) {
|
||||
);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
setResult(`Error: ${error.message}`);
|
||||
setResult(`${t('duplicate.error')}: ${error.message}`);
|
||||
} else {
|
||||
setResult('An unknown error occurred');
|
||||
setResult(t('duplicate.unknownError'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { Box } from '@mui/material';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ToolTextInput from '@components/input/ToolTextInput';
|
||||
import ToolTextResult from '@components/result/ToolTextResult';
|
||||
import {
|
||||
@@ -14,6 +15,7 @@ import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
|
||||
import SelectWithDesc from '@components/options/SelectWithDesc';
|
||||
import ToolContent from '@components/ToolContent';
|
||||
import { ToolComponentProps } from '@tools/defineTool';
|
||||
import { ParseKeys } from 'i18next';
|
||||
|
||||
const initialValues = {
|
||||
splitSeparatorType: 'symbol' as SplitOperatorType,
|
||||
@@ -25,23 +27,24 @@ const initialValues = {
|
||||
trimItems: false
|
||||
};
|
||||
const splitOperators: {
|
||||
title: string;
|
||||
description: string;
|
||||
title: ParseKeys<'list'>;
|
||||
description: ParseKeys<'list'>;
|
||||
type: SplitOperatorType;
|
||||
}[] = [
|
||||
{
|
||||
title: 'Use a Symbol for Splitting',
|
||||
description: 'Delimit input list items with a character.',
|
||||
title: 'findMostPopular.splitOperators.symbol.title',
|
||||
description: 'findMostPopular.splitOperators.symbol.description',
|
||||
type: 'symbol'
|
||||
},
|
||||
{
|
||||
title: 'Use a Regex for Splitting',
|
||||
title: 'findMostPopular.splitOperators.regex.title',
|
||||
type: 'regex',
|
||||
description: 'Delimit input list items with a regular expression.'
|
||||
description: 'findMostPopular.splitOperators.regex.description'
|
||||
}
|
||||
];
|
||||
|
||||
export default function FindMostPopular({ title }: ToolComponentProps) {
|
||||
const { t } = useTranslation('list');
|
||||
const [input, setInput] = useState<string>('');
|
||||
const [result, setResult] = useState<string>('');
|
||||
const compute = (optionsValues: typeof initialValues, input: any) => {
|
||||
@@ -74,28 +77,35 @@ export default function FindMostPopular({ title }: ToolComponentProps) {
|
||||
title={title}
|
||||
input={input}
|
||||
inputComponent={
|
||||
<ToolTextInput title={'Input list'} value={input} onChange={setInput} />
|
||||
<ToolTextInput
|
||||
title={t('findMostPopular.inputTitle')}
|
||||
value={input}
|
||||
onChange={setInput}
|
||||
/>
|
||||
}
|
||||
resultComponent={
|
||||
<ToolTextResult title={'Most popular items'} value={result} />
|
||||
<ToolTextResult
|
||||
title={t('findMostPopular.resultTitle')}
|
||||
value={result}
|
||||
/>
|
||||
}
|
||||
initialValues={initialValues}
|
||||
getGroups={({ values, updateField }) => [
|
||||
{
|
||||
title: 'How to Extract List Items?',
|
||||
title: t('findMostPopular.extractListItems'),
|
||||
component: (
|
||||
<Box>
|
||||
{splitOperators.map(({ title, description, type }) => (
|
||||
<SimpleRadio
|
||||
key={type}
|
||||
onClick={() => updateField('splitSeparatorType', type)}
|
||||
title={title}
|
||||
description={description}
|
||||
title={t(title)}
|
||||
description={t(description)}
|
||||
checked={values.splitSeparatorType === type}
|
||||
/>
|
||||
))}
|
||||
<TextFieldWithDesc
|
||||
description={'Set a delimiting symbol or regular expression.'}
|
||||
description={t('findMostPopular.splitSeparatorDescription')}
|
||||
value={values.splitSeparator}
|
||||
onOwnChange={(val) => updateField('splitSeparator', val)}
|
||||
/>
|
||||
@@ -103,26 +113,24 @@ export default function FindMostPopular({ title }: ToolComponentProps) {
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Item comparison',
|
||||
title: t('findMostPopular.itemComparison'),
|
||||
component: (
|
||||
<Box>
|
||||
<CheckboxWithDesc
|
||||
title={'Remove empty items'}
|
||||
description={'Ignore empty items from comparison.'}
|
||||
title={t('findMostPopular.removeEmptyItems')}
|
||||
description={t('findMostPopular.removeEmptyItemsDescription')}
|
||||
checked={values.deleteEmptyItems}
|
||||
onChange={(value) => updateField('deleteEmptyItems', value)}
|
||||
/>
|
||||
<CheckboxWithDesc
|
||||
title={'Trim top list items'}
|
||||
description={
|
||||
'Remove leading and trailing spaces before comparing items'
|
||||
}
|
||||
title={t('findMostPopular.trimItems')}
|
||||
description={t('findMostPopular.trimItemsDescription')}
|
||||
checked={values.trimItems}
|
||||
onChange={(value) => updateField('trimItems', value)}
|
||||
/>
|
||||
<CheckboxWithDesc
|
||||
title={'Ignore Item Case'}
|
||||
description={'Compare all list items in lowercase.'}
|
||||
title={t('findMostPopular.ignoreItemCase')}
|
||||
description={t('findMostPopular.ignoreItemCaseDescription')}
|
||||
checked={values.ignoreItemCase}
|
||||
onChange={(value) => updateField('ignoreItemCase', value)}
|
||||
/>
|
||||
@@ -130,27 +138,42 @@ export default function FindMostPopular({ title }: ToolComponentProps) {
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Top item output format',
|
||||
title: t('findMostPopular.outputFormat'),
|
||||
component: (
|
||||
<Box>
|
||||
<SelectWithDesc
|
||||
selected={values.displayFormat}
|
||||
options={[
|
||||
{ label: 'Show item percentage', value: 'percentage' },
|
||||
{ label: 'Show item count', value: 'count' },
|
||||
{ label: 'Show item total', value: 'total' }
|
||||
{
|
||||
label: t('findMostPopular.displayOptions.percentage'),
|
||||
value: 'percentage'
|
||||
},
|
||||
{
|
||||
label: t('findMostPopular.displayOptions.count'),
|
||||
value: 'count'
|
||||
},
|
||||
{
|
||||
label: t('findMostPopular.displayOptions.total'),
|
||||
value: 'total'
|
||||
}
|
||||
]}
|
||||
onChange={(value) => updateField('displayFormat', value)}
|
||||
description={'How to display the most popular list items?'}
|
||||
description={t('findMostPopular.displayFormatDescription')}
|
||||
/>
|
||||
<SelectWithDesc
|
||||
selected={values.sortingMethod}
|
||||
options={[
|
||||
{ label: 'Sort Alphabetically', value: 'alphabetic' },
|
||||
{ label: 'Sort by count', value: 'count' }
|
||||
{
|
||||
label: t('findMostPopular.sortOptions.alphabetic'),
|
||||
value: 'alphabetic'
|
||||
},
|
||||
{
|
||||
label: t('findMostPopular.sortOptions.count'),
|
||||
value: 'count'
|
||||
}
|
||||
]}
|
||||
onChange={(value) => updateField('sortingMethod', value)}
|
||||
description={'Select a sorting method.'}
|
||||
description={t('findMostPopular.sortingMethodDescription')}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
|
@@ -12,6 +12,7 @@ import { ToolComponentProps } from '@tools/defineTool';
|
||||
import { FormikProps } from 'formik';
|
||||
import ToolContent from '@components/ToolContent';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ParseKeys } from 'i18next';
|
||||
|
||||
const initialValues = {
|
||||
splitSeparatorType: 'symbol' as SplitOperatorType,
|
||||
@@ -25,55 +26,45 @@ const initialValues = {
|
||||
charAfterChunk: ''
|
||||
};
|
||||
const splitOperators: {
|
||||
title: string;
|
||||
description: string;
|
||||
title: ParseKeys<'string'>;
|
||||
description: ParseKeys<'string'>;
|
||||
type: SplitOperatorType;
|
||||
}[] = [
|
||||
{
|
||||
title: 'Use a Symbol for Splitting',
|
||||
description:
|
||||
'Character that will be used to\n' +
|
||||
'break text into parts.\n' +
|
||||
'(Space by default.)',
|
||||
title: 'split.symbolTitle',
|
||||
description: 'split.symbolDescription',
|
||||
type: 'symbol'
|
||||
},
|
||||
{
|
||||
title: 'Use a Regex for Splitting',
|
||||
title: 'split.regexTitle',
|
||||
type: 'regex',
|
||||
description:
|
||||
'Regular expression that will be\n' +
|
||||
'used to break text into parts.\n' +
|
||||
'(Multiple spaces by default.)'
|
||||
description: 'split.regexDescription'
|
||||
},
|
||||
{
|
||||
title: 'Use Length for Splitting',
|
||||
description:
|
||||
'Number of symbols that will be\n' + 'put in each output chunk.',
|
||||
title: 'split.lengthTitle',
|
||||
description: 'split.lengthDescription',
|
||||
type: 'length'
|
||||
},
|
||||
{
|
||||
title: 'Use a Number of Chunks',
|
||||
description: 'Number of chunks of equal\n' + 'length in the output.',
|
||||
title: 'split.chunksTitle',
|
||||
description: 'split.chunksDescription',
|
||||
type: 'chunks'
|
||||
}
|
||||
];
|
||||
const outputOptions: {
|
||||
description: string;
|
||||
description: ParseKeys<'string'>;
|
||||
accessor: keyof typeof initialValues;
|
||||
}[] = [
|
||||
{
|
||||
description:
|
||||
'Character that will be put\n' +
|
||||
'between the split chunks.\n' +
|
||||
'(It\'s newline "\\n" by default.)',
|
||||
description: 'split.outputSeparatorDescription',
|
||||
accessor: 'outputSeparator'
|
||||
},
|
||||
{
|
||||
description: 'Character before each chunk',
|
||||
description: 'split.charBeforeChunkDescription',
|
||||
accessor: 'charBeforeChunk'
|
||||
},
|
||||
{
|
||||
description: 'Character after each chunk',
|
||||
description: 'split.charAfterChunkDescription',
|
||||
accessor: 'charAfterChunk'
|
||||
}
|
||||
];
|
||||
@@ -183,9 +174,9 @@ export default function SplitText({ title }: ToolComponentProps) {
|
||||
<RadioWithTextField
|
||||
key={type}
|
||||
checked={type === values.splitSeparatorType}
|
||||
title={t(`split.${type}Title`)}
|
||||
title={t(title)}
|
||||
fieldName={'splitSeparatorType'}
|
||||
description={t(`split.${type}Description`)}
|
||||
description={t(description)}
|
||||
value={values[`${type}Value`]}
|
||||
onRadioClick={() => updateField('splitSeparatorType', type)}
|
||||
onTextChange={(val) => updateField(`${type}Value`, val)}
|
||||
@@ -199,8 +190,7 @@ export default function SplitText({ title }: ToolComponentProps) {
|
||||
key={option.accessor}
|
||||
value={values[option.accessor]}
|
||||
onOwnChange={(value) => updateField(option.accessor, value)}
|
||||
//@ts-ignore
|
||||
description={t(`split.${option.accessor}Description`)}
|
||||
description={t(option.description)}
|
||||
/>
|
||||
))
|
||||
}
|
||||
|
Reference in New Issue
Block a user