feat: add internationalization support

This commit is contained in:
AshAnand34
2025-07-12 23:02:35 -07:00
parent 3b702b260c
commit f22bb8bd57
149 changed files with 2807 additions and 1045 deletions

View File

@@ -9,6 +9,7 @@ import ToolFileResult from '@components/result/ToolFileResult';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import RadioWithTextField from '@components/options/RadioWithTextField';
import { changeAudioSpeed } from './service';
import { useTranslation } from 'react-i18next';
const initialValues: InitialValuesType = {
newSpeed: 2,
@@ -25,6 +26,7 @@ export default function ChangeSpeed({
title,
longDescription
}: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<File | null>(null);
const [result, setResult] = useState<File | null>(null);
const [loading, setLoading] = useState(false);
@@ -49,20 +51,20 @@ export default function ChangeSpeed({
updateField
}) => [
{
title: 'New Audio Speed',
title: t('audio.changeSpeed.newAudioSpeed'),
component: (
<Box>
<TextFieldWithDesc
value={values.newSpeed.toString()}
onOwnChange={(val) => updateField('newSpeed', Number(val))}
description="Default multiplier: 2 means 2x faster"
description={t('audio.changeSpeed.speedDescription')}
type="number"
/>
</Box>
)
},
{
title: 'Output Format',
title: t('audio.changeSpeed.outputFormat'),
component: (
<Box mt={2}>
<RadioGroup
@@ -96,15 +98,19 @@ export default function ChangeSpeed({
<ToolAudioInput
value={input}
onChange={setInput}
title={'Input Audio'}
title={t('audio.changeSpeed.inputTitle')}
/>
}
resultComponent={
loading ? (
<ToolFileResult title="Setting Speed" value={null} loading={true} />
<ToolFileResult
title={t('audio.changeSpeed.settingSpeed')}
value={null}
loading={true}
/>
) : (
<ToolFileResult
title="Edited Audio"
title={t('audio.changeSpeed.resultTitle')}
value={result}
extension={result ? result.name.split('.').pop() : undefined}
/>
@@ -114,7 +120,10 @@ export default function ChangeSpeed({
getGroups={getGroups}
setInput={setInput}
compute={compute}
toolInfo={{ title: `What is ${title}?`, description: longDescription }}
toolInfo={{
title: t('audio.changeSpeed.toolInfo.title', { title }),
description: longDescription
}}
/>
);
}

View File

@@ -2,12 +2,28 @@ import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('audio', {
name: 'Change speed',
name: 'Change audio speed',
path: 'change-speed',
icon: 'material-symbols-light:speed-outline',
icon: 'material-symbols:speed',
description:
'This online utility lets you change the speed of an audio. You can speed it up or slow it down.',
shortDescription: 'Quickly change audio speed',
keywords: ['change', 'speed'],
component: lazy(() => import('./index'))
'Change the playback speed of audio files. Speed up or slow down audio while maintaining pitch.',
shortDescription: 'Change the speed of audio files',
keywords: [
'audio',
'speed',
'tempo',
'playback',
'accelerate',
'slow down',
'pitch',
'media'
],
longDescription:
'This tool allows you to change the playback speed of audio files. You can speed up or slow down audio while maintaining the original pitch. Useful for podcasts, music, or any audio content where you want to adjust the playback speed.',
component: lazy(() => import('./index')),
i18n: {
name: 'audio.changeSpeed.name',
description: 'audio.changeSpeed.description',
shortDescription: 'audio.changeSpeed.shortDescription'
}
});

View File

@@ -8,6 +8,7 @@ import ToolVideoInput from '@components/input/ToolVideoInput';
import { GetGroupsType } from '@components/options/ToolOptions';
import ToolFileResult from '@components/result/ToolFileResult';
import SelectWithDesc from '@components/options/SelectWithDesc';
import { useTranslation } from 'react-i18next';
const initialValues: InitialValuesType = {
outputFormat: 'aac'
@@ -17,6 +18,7 @@ export default function ExtractAudio({
title,
longDescription
}: ToolComponentProps) {
const { t } = useTranslation();
const [file, setFile] = useState<File | null>(null);
const [audioFile, setAudioFile] = useState<File | null>(null);
const [loading, setLoading] = useState(false);
@@ -27,7 +29,7 @@ export default function ExtractAudio({
}) => {
return [
{
title: 'Output Format',
title: t('audio.extractAudio.outputFormat'),
component: (
<Box>
<SelectWithDesc
@@ -40,9 +42,7 @@ export default function ExtractAudio({
{ label: 'MP3', value: 'mp3' },
{ label: 'WAV', value: 'wav' }
]}
description={
'Select the format for the audio to be extracted as.'
}
description={t('audio.extractAudio.outputFormatDescription')}
/>
</Box>
)
@@ -68,23 +68,33 @@ export default function ExtractAudio({
title={title}
input={file}
inputComponent={
<ToolVideoInput value={file} onChange={setFile} title={'Input Video'} />
<ToolVideoInput
value={file}
onChange={setFile}
title={t('audio.extractAudio.inputTitle')}
/>
}
resultComponent={
loading ? (
<ToolFileResult
title={'Extracting Audio'}
title={t('audio.extractAudio.extractingAudio')}
value={null}
loading={true}
/>
) : (
<ToolFileResult title={'Extracted Audio'} value={audioFile} />
<ToolFileResult
title={t('audio.extractAudio.resultTitle')}
value={audioFile}
/>
)
}
initialValues={initialValues}
getGroups={getGroups}
compute={compute}
toolInfo={{ title: `What is ${title}?`, description: longDescription }}
toolInfo={{
title: t('audio.extractAudio.toolInfo.title', { title }),
description: longDescription
}}
setInput={setFile}
/>
);

View File

@@ -22,5 +22,10 @@ export const tool = defineTool('audio', {
],
longDescription:
'This tool allows you to extract the audio track from a video file (such as MP4, MOV, AVI, etc.) and save it as a standalone audio file in your preferred format (AAC, MP3, or WAV). Useful for podcasts, music, or any scenario where you need just the audio from a video.',
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'audio.extractAudio.name',
description: 'audio.extractAudio.description',
shortDescription: 'audio.extractAudio.shortDescription'
}
});

View File

@@ -0,0 +1,28 @@
{
"changeSpeed": {
"title": "Change Audio Speed",
"description": "Change the playback speed of audio files.",
"inputTitle": "Input Audio",
"resultTitle": "Modified Audio",
"speedOptions": "Speed Options",
"speedDescription": "Speed multiplier (0.5 = half speed, 2.0 = double speed)",
"speedPlaceholder": "Speed",
"toolInfo": {
"title": "Change audio speed",
"description": "This tool allows you to change the playback speed of audio files. You can slow down or speed up audio while maintaining pitch quality."
}
},
"extractAudio": {
"title": "Extract Audio from Video",
"description": "Extract audio track from video files.",
"inputTitle": "Input Video",
"resultTitle": "Extracted Audio",
"outputFormat": "Output Format",
"outputFormatDescription": "Select the format for the audio to be extracted as.",
"extractingAudio": "Extracting Audio",
"toolInfo": {
"title": "What is {{title}}?",
"description": "This tool allows you to extract the audio track from video files. You can choose from different audio formats including AAC, MP3, and WAV."
}
}
}

View File

@@ -2,14 +2,17 @@ import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('csv', {
name: 'Change CSV separator',
name: 'Change CSV Separator',
path: 'change-csv-separator',
icon: 'material-symbols:split-scene-rounded',
icon: 'material-symbols:code',
description:
'Just upload your CSV file in the form below and it will automatically get a new column delimiter character. In the tool options, you can specify which delimiter and quote characters are used in the source CSV file and customize the desired delimiter and quote characters for the output CSV. You can also filter the input CSV before the conversion process and skip blank lines and comment lines.',
shortDescription: 'Quickly change the CSV column delimiter to a new symbol.',
keywords: ['change', 'csv', 'sepa rator'],
longDescription:
'This tool changes the field separator in CSV (Comma Separated Values) files. This is useful because different programs may use different default separators. While a comma is the most common separator in CSV files, some programs require files to be tab-separated (TSV), semicolon-separated (SSV), pipe-separated (PSV), or have another separation symbol. The default comma may not be so convenient as a delimiter in CSV files because commas are frequently present within fields. In such cases, it can be difficult and confusing to distinguish between commas as delimiters and commas as punctuation symbols. By replacing the comma with another delimiter, you can convert the file into a more easily readable and parsable format. In the options section of this tool, you can configure both the input and output CSV file formats. For the input CSV, you can specify its current delimiter (by default, it is a comma) and also indicate the quotation mark character used to wrap fields. For the output CSV, you can set a new delimiter, choose a new quotation mark character, and optionally enclose all the fields in quotes. Additionally, you have the option to remove empty lines from the input CSV and eliminate comment lines that start with a specified character (usually a hash "#" or double slashes "//"). Csv-abulous!',
component: lazy(() => import('./index'))
'Change the delimiter/separator in CSV files. Convert between different CSV formats like comma, semicolon, tab, or custom separators.',
shortDescription: 'Change CSV file delimiter',
keywords: ['csv', 'separator', 'delimiter', 'change'],
component: lazy(() => import('./index')),
i18n: {
name: 'csv.changeCsvSeparator.name',
description: 'csv.changeCsvSeparator.description',
shortDescription: 'csv.changeCsvSeparator.shortDescription'
}
});

View File

@@ -6,10 +6,9 @@ import { convertCsvToJson } from './service';
import { CardExampleType } from '@components/examples/ToolExamples';
import { ToolComponentProps } from '@tools/defineTool';
import { Box } from '@mui/material';
import RadioWithTextField from '@components/options/RadioWithTextField';
import SimpleRadio from '@components/options/SimpleRadio';
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import { useTranslation } from 'react-i18next';
type InitialValuesType = {
delimiter: string;
@@ -114,6 +113,7 @@ id,name,active
];
export default function CsvToJson({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -148,28 +148,36 @@ export default function CsvToJson({ title }: ToolComponentProps) {
compute={compute}
exampleCards={exampleCards}
inputComponent={
<ToolTextInput title="Input CSV" value={input} onChange={setInput} />
<ToolTextInput
title={t('csv.csvToJson.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult title="Output JSON" value={result} extension={'json'} />
<ToolTextResult
title={t('csv.csvToJson.resultTitle')}
value={result}
extension={'json'}
/>
}
getGroups={({ values, updateField }) => [
{
title: 'Input CSV Format',
title: t('csv.csvToJson.inputCsvFormat'),
component: (
<Box>
<TextFieldWithDesc
description="Column Separator"
description={t('csv.csvToJson.columnSeparator')}
value={values.delimiter}
onOwnChange={(val) => updateField('delimiter', val)}
/>
<TextFieldWithDesc
description="Field Quote"
description={t('csv.csvToJson.fieldQuote')}
onOwnChange={(val) => updateField('quote', val)}
value={values.quote}
/>
<TextFieldWithDesc
description="Comment Symbol"
description={t('csv.csvToJson.commentSymbol')}
value={values.comment}
onOwnChange={(val) => updateField('comment', val)}
/>
@@ -177,26 +185,26 @@ export default function CsvToJson({ title }: ToolComponentProps) {
)
},
{
title: 'Conversion Options',
title: t('csv.csvToJson.conversionOptions'),
component: (
<Box>
<CheckboxWithDesc
checked={values.useHeaders}
onChange={(value) => updateField('useHeaders', value)}
title="Use Headers"
description="First row is treated as column headers"
title={t('csv.csvToJson.useHeaders')}
description={t('csv.csvToJson.useHeadersDescription')}
/>
<CheckboxWithDesc
checked={values.skipEmptyLines}
onChange={(value) => updateField('skipEmptyLines', value)}
title="Skip Empty Lines"
description="Don't process empty lines in the CSV"
title={t('csv.csvToJson.skipEmptyLines')}
description={t('csv.csvToJson.skipEmptyLinesDescription')}
/>
<CheckboxWithDesc
checked={values.dynamicTypes}
onChange={(value) => updateField('dynamicTypes', value)}
title="Dynamic Types"
description="Convert numbers and booleans to their proper types"
title={t('csv.csvToJson.dynamicTypes')}
description={t('csv.csvToJson.dynamicTypesDescription')}
/>
</Box>
)

View File

@@ -10,6 +10,7 @@ import { findIncompleteCsvRecords } from './service';
import { InitialValuesType } from './types';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
import { useTranslation } from 'react-i18next';
const initialValues: InitialValuesType = {
csvSeparator: ',',
@@ -103,6 +104,7 @@ export default function FindIncompleteCsvRecords({
title,
longDescription
}: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -115,55 +117,59 @@ export default function FindIncompleteCsvRecords({
updateField
}) => [
{
title: 'Csv input Options',
title: t('csv.findIncompleteCsvRecords.csvInputOptions'),
component: (
<Box>
<TextFieldWithDesc
value={values.csvSeparator}
onOwnChange={(val) => updateField('csvSeparator', val)}
description={
'Enter the character used to delimit columns in the CSV input file.'
}
description={t(
'csv.findIncompleteCsvRecords.csvSeparatorDescription'
)}
/>
<TextFieldWithDesc
value={values.quoteCharacter}
onOwnChange={(val) => updateField('quoteCharacter', val)}
description={
'Enter the quote character used to quote the CSV input fields.'
}
description={t(
'csv.findIncompleteCsvRecords.quoteCharacterDescription'
)}
/>
<TextFieldWithDesc
value={values.commentCharacter}
onOwnChange={(val) => updateField('commentCharacter', val)}
description={
'Enter the character indicating the start of a comment line. Lines starting with this symbol will be skipped.'
}
description={t(
'csv.findIncompleteCsvRecords.commentCharacterDescription'
)}
/>
</Box>
)
},
{
title: 'Checking Options',
title: t('csv.findIncompleteCsvRecords.checkingOptions'),
component: (
<Box>
<CheckboxWithDesc
checked={values.emptyLines}
onChange={(value) => updateField('emptyLines', value)}
title="Delete Lines with No Data"
description="Remove empty lines from CSV input file."
title={t('csv.findIncompleteCsvRecords.deleteLinesWithNoData')}
description={t(
'csv.findIncompleteCsvRecords.deleteLinesWithNoDataDescription'
)}
/>
<CheckboxWithDesc
checked={values.emptyValues}
onChange={(value) => updateField('emptyValues', value)}
title="Find Empty Values"
description="Display a message about CSV fields that are empty (These are not missing fields but fields that contain nothing)."
title={t('csv.findIncompleteCsvRecords.findEmptyValues')}
description={t(
'csv.findIncompleteCsvRecords.findEmptyValuesDescription'
)}
/>
<CheckboxWithDesc
checked={values.messageLimit}
onChange={(value) => updateField('messageLimit', value)}
title="Limit number of messages"
title={t('csv.findIncompleteCsvRecords.limitNumberOfMessages')}
/>
{values.messageLimit && (
@@ -172,7 +178,9 @@ export default function FindIncompleteCsvRecords({
onOwnChange={(val) => updateField('messageNumber', Number(val))}
type="number"
inputProps={{ min: 1 }}
description={'Set the limit of number of messages in the output.'}
description={t(
'csv.findIncompleteCsvRecords.messageLimitDescription'
)}
/>
)}
</Box>
@@ -184,15 +192,27 @@ export default function FindIncompleteCsvRecords({
title={title}
input={input}
inputComponent={
<ToolTextInput title={'Input CSV'} value={input} onChange={setInput} />
<ToolTextInput
title={t('csv.findIncompleteCsvRecords.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult
title={t('csv.findIncompleteCsvRecords.resultTitle')}
value={result}
/>
}
resultComponent={<ToolTextResult title={'CSV Status'} value={result} />}
initialValues={initialValues}
exampleCards={exampleCards}
getGroups={getGroups}
setInput={setInput}
compute={compute}
toolInfo={{ title: `What is a ${title}?`, description: longDescription }}
toolInfo={{
title: t('csv.findIncompleteCsvRecords.toolInfo.title', { title }),
description: longDescription
}}
/>
);
}

View File

@@ -11,6 +11,11 @@ export const tool = defineTool('csv', {
'Quickly find rows and columns in CSV that are missing values.',
keywords: ['find', 'incomplete', 'csv', 'records'],
longDescription:
'This tool checks the completeness of CSV (Comma Separated Values) files and identifies incomplete records within the data. It finds rows and columns where one or more values are missing and displays their positions in the output so that you can quickly find and fix your CSV file. A valid CSV file has the same number of values (fields) in all rows and the same number of values (fields) in all columns. If the CSV you load in this tool is complete, the program will notify you with a green badge. If at least one value is missing in any row or column, the program will show a red badge and indicate the exact location of the missing value. If the CSV file has a field with no characters in it, then such a field is called an empty field. It is not a missing field, just empty as it contains nothing. You can activate the "Find Empty Values" checkbox in the options to identify all such fields in the CSV. If the file contains empty lines, you can ignore them with the "Skip Empty Lines" option or check them for completeness along with other lines. You can also configure the delimiter, quote, and comment characters in the options. This allows you to adapt to other file formats besides CSV, such as TSV (Tab Separated Values), SSV (Semicolon Separated Values), or PSV (Pipe Separated Values). If the file has too many incomplete or empty records, you can set a limit on the output messages to display, for example, 5, 10, or 20 messages. If you want to quickly fill in the missing data with default values, you can use our Fill Incomplete CSV Records tool. Csv-abulous!',
component: lazy(() => import('./index'))
'This tool allows you to find incomplete or missing records in CSV data. It can detect missing columns, empty values, and other data quality issues in your CSV files. You can customize the CSV parsing options and set limits on error reporting.',
component: lazy(() => import('./index')),
i18n: {
name: 'csv.findIncompleteCsvRecords.name',
description: 'csv.findIncompleteCsvRecords.description',
shortDescription: 'csv.findIncompleteCsvRecords.shortDescription'
}
});

View File

@@ -0,0 +1,63 @@
{
"csvToJson": {
"title": "CSV to JSON",
"description": "Convert CSV data to JSON format.",
"inputTitle": "Input CSV",
"resultTitle": "JSON Output",
"csvOptions": "CSV Options",
"separatorDescription": "Character used to separate columns",
"quoteCharDescription": "Character used to quote fields",
"commentCharDescription": "Character that indicates comment lines",
"jsonOptions": "JSON Options",
"prettyPrintDescription": "Format JSON with indentation",
"toolInfo": {
"title": "CSV to JSON Converter",
"description": "This tool allows you to convert CSV (Comma-Separated Values) data to JSON format. You can customize the CSV parsing options and JSON output format."
}
},
"findIncompleteCsvRecords": {
"title": "Find Incomplete CSV Records",
"description": "Identify CSV records with missing or incomplete data.",
"inputTitle": "Input CSV",
"resultTitle": "Incomplete Records",
"csvOptions": "CSV Options",
"separatorDescription": "Character used to separate columns",
"quoteCharDescription": "Character used to quote fields",
"commentCharDescription": "Character that indicates comment lines",
"toolInfo": {
"title": "Find Incomplete CSV Records",
"description": "This tool helps you identify CSV records that have missing or incomplete data. It's useful for data validation and cleaning."
}
},
"insertCsvColumns": {
"title": "Insert CSV Columns",
"description": "Add new columns to CSV data at specified positions.",
"inputTitle": "Input CSV",
"resultTitle": "Output CSV",
"csvToInsert": "CSV to insert",
"csvSeparator": "CSV separator",
"csvToInsertDescription": "Enter one or more columns you want to insert into the CSV. the character used to delimit columns has to be the same with the one in the CSV input file. Ps: Blank lines will be ignored",
"csvOptions": "CSV Options",
"separatorDescription": "Enter the character used to delimit columns in the CSV input file.",
"quoteCharDescription": "Enter the quote character used to quote the CSV input fields.",
"commentCharacterDescription": "Enter the character indicating the start of a comment line. Lines starting with this symbol will be skipped.",
"fillWithEmptyValues": "Fill With Empty Values",
"fillWithCustomValues": "Fill With Customs Values",
"customFillDescription": "If the input CSV file is incomplete (missing values), then add empty fields or custom symbols to records to make a well-formed CSV?",
"customFillValueDescription": "Use this custom value to fill in missing fields. (Works only with \"Custom Values\" mode above.)",
"positionOptions": "Position Options",
"prependColumns": "Prepend columns",
"appendColumns": "Append columns",
"customPosition": "Custom position",
"insertingPositionDescription": "Specify where to insert the columns in the CSV file.",
"headerName": "Header name",
"position": "Position",
"customPositionOptionsDescription": "Select the method to insert the columns in the CSV file.",
"headerNameDescription": "Header of the column you want to insert columns after.",
"rowNumberDescription": "Number of the column you want to insert columns after.",
"toolInfo": {
"title": "Insert CSV Columns",
"description": "This tool allows you to insert new columns into CSV data at specified positions. You can prepend, append, or insert columns at custom positions based on header names or column numbers."
}
}
}

View File

@@ -1,16 +1,17 @@
import { Box } from '@mui/material';
import React, { useState } from 'react';
import ToolContent from '@components/ToolContent';
import { ToolComponentProps } from '@tools/defineTool';
import ToolTextInput from '@components/input/ToolTextInput';
import ToolTextResult from '@components/result/ToolTextResult';
import { GetGroupsType } from '@components/options/ToolOptions';
import { CardExampleType } from '@components/examples/ToolExamples';
import { main } from './service';
import { getCsvHeaders } from '@utils/csv';
import { InitialValuesType } from './types';
import { GetGroupsType } from '@components/options/ToolOptions';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import SelectWithDesc from '@components/options/SelectWithDesc';
import { CardExampleType } from '@components/examples/ToolExamples';
import { ToolComponentProps } from '@tools/defineTool';
import { getCsvHeaders } from '@utils/csv';
import { useTranslation } from 'react-i18next';
const initialValues: InitialValuesType = {
csvToInsert: '',
@@ -27,15 +28,19 @@ const initialValues: InitialValuesType = {
const exampleCards: CardExampleType<InitialValuesType>[] = [
{
title: 'Add One Column to a CSV File',
title: 'Insert a single column by position',
description:
'In this example, we insert a column with the title "city" into a CSV file that already contains two other columns with titles "name" and "age". The new column consists of three values: "city", "dallas", and "houston", corresponding to the height of the input CSV data. The value "city" is the header value (appearing on the first row) and values "dallas" and "houston" are data values (appearing on rows two and three). We specify the position of the new column by an ordinal number and set it to 1 in the options. This value indicates that the new "city" column should be placed after the first column.',
sampleText: `name,age
john,25
emma,22`,
sampleResult: `name,city,age
john,dallas,25
emma,houston,22`,
'In this example, we insert a single column "city" at position 1 in the CSV data. The input CSV has data about cars, including the "Brand" and "Model" of the car. We now add a "city" column at position 1. To do this, we enter the city data in the comma-separated format in the "New Column" option, and to quickly add the new column at position 1, then we specify the position number.',
sampleText: `Brand,Model
Toyota,Camry
Ford,Mustang
Honda,Accord
Chevrolet,Malibu`,
sampleResult: `city,Brand,Model
dallas,Toyota,Camry
houston,Ford,Mustang
dallas,Honda,Accord
houston,Chevrolet,Malibu`,
sampleOptions: {
csvToInsert: `city
dallas
@@ -118,6 +123,7 @@ export default function InsertCsvColumns({
title,
longDescription
}: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -139,7 +145,7 @@ export default function InsertCsvColumns({
updateField
}) => [
{
title: 'CSV to insert',
title: t('csv.insertCsvColumns.csvToInsert'),
component: (
<Box>
<TextFieldWithDesc
@@ -147,44 +153,42 @@ export default function InsertCsvColumns({
rows={3}
value={values.csvToInsert}
onOwnChange={(val) => updateField('csvToInsert', val)}
title="CSV separator"
description={`Enter one or more columns you want to insert into the CSV.
the character used to delimit columns has to be the same with the one in the CSV input file.
Ps: Blank lines will be ignored`}
title={t('csv.insertCsvColumns.csvSeparator')}
description={t('csv.insertCsvColumns.csvToInsertDescription')}
/>
</Box>
)
},
{
title: 'CSV Options',
title: t('csv.insertCsvColumns.csvOptions'),
component: (
<Box>
<TextFieldWithDesc
value={values.separator}
onOwnChange={(val) => updateField('separator', val)}
description={
'Enter the character used to delimit columns in the CSV input file.'
}
description={t('csv.insertCsvColumns.separatorDescription')}
/>
<TextFieldWithDesc
value={values.quoteChar}
onOwnChange={(val) => updateField('quoteChar', val)}
description={
'Enter the quote character used to quote the CSV input fields.'
}
description={t('csv.insertCsvColumns.quoteCharDescription')}
/>
<TextFieldWithDesc
value={values.commentCharacter}
onOwnChange={(val) => updateField('commentCharacter', val)}
description={
'Enter the character indicating the start of a comment line. Lines starting with this symbol will be skipped.'
}
description={t('csv.insertCsvColumns.commentCharacterDescription')}
/>
<SelectWithDesc
selected={values.customFill}
options={[
{ label: 'Fill With Empty Values', value: false },
{ label: 'Fill With Customs Values', value: true }
{
label: t('csv.insertCsvColumns.fillWithEmptyValues'),
value: false
},
{
label: t('csv.insertCsvColumns.fillWithCustomValues'),
value: true
}
]}
onChange={(value) => {
updateField('customFill', value);
@@ -192,48 +196,59 @@ export default function InsertCsvColumns({
updateField('customFillValue', ''); // Reset custom fill value
}
}}
description={
'If the input CSV file is incomplete (missing values), then add empty fields or custom symbols to records to make a well-formed CSV?'
}
description={t('csv.insertCsvColumns.customFillDescription')}
/>
{values.customFill && (
<TextFieldWithDesc
value={values.customFillValue}
onOwnChange={(val) => updateField('customFillValue', val)}
description={
'Use this custom value to fill in missing fields. (Works only with "Custom Values" mode above.)'
}
description={t('csv.insertCsvColumns.customFillValueDescription')}
/>
)}
</Box>
)
},
{
title: 'Position Options',
title: t('csv.insertCsvColumns.positionOptions'),
component: (
<Box>
<SelectWithDesc
selected={values.insertingPosition}
options={[
{ label: 'Prepend columns', value: 'prepend' },
{ label: 'Append columns', value: 'append' },
{ label: 'Custom position', value: 'custom' }
{
label: t('csv.insertCsvColumns.prependColumns'),
value: 'prepend'
},
{
label: t('csv.insertCsvColumns.appendColumns'),
value: 'append'
},
{
label: t('csv.insertCsvColumns.customPosition'),
value: 'custom'
}
]}
onChange={(value) => updateField('insertingPosition', value)}
description={'Specify where to insert the columns in the CSV file.'}
description={t('csv.insertCsvColumns.insertingPositionDescription')}
/>
{values.insertingPosition === 'custom' && (
<SelectWithDesc
selected={values.customPostionOptions}
options={[
{ label: 'Header name', value: 'headerName' },
{ label: 'Position ', value: 'rowNumber' }
{
label: t('csv.insertCsvColumns.headerName'),
value: 'headerName'
},
{
label: t('csv.insertCsvColumns.position'),
value: 'rowNumber'
}
]}
onChange={(value) => updateField('customPostionOptions', value)}
description={
'Select the method to insert the columns in the CSV file.'
}
description={t(
'csv.insertCsvColumns.customPositionOptionsDescription'
)}
/>
)}
@@ -243,42 +258,53 @@ export default function InsertCsvColumns({
selected={values.headerName}
options={headerOptions}
onChange={(value) => updateField('headerName', value)}
description={
'Header of the column you want to insert columns after.'
}
description={t('csv.insertCsvColumns.headerNameDescription')}
/>
)}
{values.insertingPosition === 'custom' &&
values.customPostionOptions === 'rowNumber' && (
<TextFieldWithDesc
value={values.rowNumber}
onOwnChange={(val) => updateField('rowNumber', Number(val))}
inputProps={{ min: 1, max: headers.length }}
type="number"
description={
'Number of the column you want to insert columns after.'
value={values.rowNumber.toString()}
onOwnChange={(val) =>
updateField('rowNumber', parseInt(val) || 0)
}
description={t('csv.insertCsvColumns.rowNumberDescription')}
type="number"
/>
)}
</Box>
)
}
];
return (
<ToolContent
title={title}
input={input}
inputComponent={
<ToolTextInput value={input} title="Input CSV" onChange={setInput} />
<ToolTextInput
title={t('csv.insertCsvColumns.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult
title={t('csv.insertCsvColumns.resultTitle')}
value={result}
extension={'csv'}
/>
}
resultComponent={<ToolTextResult title="Output CSV" value={result} />}
initialValues={initialValues}
exampleCards={exampleCards}
getGroups={getGroups}
setInput={setInput}
compute={compute}
toolInfo={{ title: `What is a ${title}?`, description: longDescription }}
input={input}
setInput={setInput}
toolInfo={{
title: t('csv.insertCsvColumns.toolInfo.title'),
description: t('csv.insertCsvColumns.toolInfo.description')
}}
exampleCards={exampleCards}
/>
);
}

View File

@@ -11,6 +11,7 @@ import SimpleRadio from '@components/options/SimpleRadio';
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
import { processImage } from './service';
import { InitialValuesType } from './types';
import { useTranslation } from 'react-i18next';
const initialValues: InitialValuesType = {
resizeMethod: 'pixels' as 'pixels' | 'percentage',
@@ -45,6 +46,7 @@ const validationSchema = Yup.object({
});
export default function ResizeImage({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<File | null>(null);
const [result, setResult] = useState<File | null>(null);
@@ -58,22 +60,20 @@ export default function ResizeImage({ title }: ToolComponentProps) {
updateField
}) => [
{
title: 'Resize Method',
title: t('image.resize.resizeMethod'),
component: (
<Box>
<SimpleRadio
onClick={() => updateField('resizeMethod', 'pixels')}
checked={values.resizeMethod === 'pixels'}
description={'Resize by specifying dimensions in pixels.'}
title={'Resize by Pixels'}
description={t('image.resize.resizeByPixelsDescription')}
title={t('image.resize.resizeByPixels')}
/>
<SimpleRadio
onClick={() => updateField('resizeMethod', 'percentage')}
checked={values.resizeMethod === 'percentage'}
description={
'Resize by specifying a percentage of the original size.'
}
title={'Resize by Percentage'}
description={t('image.resize.resizeByPercentageDescription')}
title={t('image.resize.resizeByPercentage')}
/>
</Box>
)
@@ -81,7 +81,7 @@ export default function ResizeImage({ title }: ToolComponentProps) {
...(values.resizeMethod === 'pixels'
? [
{
title: 'Dimension Type',
title: t('image.resize.dimensionType'),
component: (
<Box>
<CheckboxWithDesc
@@ -89,35 +89,29 @@ export default function ResizeImage({ title }: ToolComponentProps) {
onChange={(value) =>
updateField('maintainAspectRatio', value)
}
description={
'Maintain the original aspect ratio of the image.'
}
title={'Maintain Aspect Ratio'}
description={t('image.resize.maintainAspectRatioDescription')}
title={t('image.resize.maintainAspectRatio')}
/>
{values.maintainAspectRatio && (
<Box>
<SimpleRadio
onClick={() => updateField('dimensionType', 'width')}
checked={values.dimensionType === 'width'}
description={
'Specify the width in pixels and calculate height based on aspect ratio.'
}
title={'Set Width'}
description={t('image.resize.setWidthDescription')}
title={t('image.resize.setWidth')}
/>
<SimpleRadio
onClick={() => updateField('dimensionType', 'height')}
checked={values.dimensionType === 'height'}
description={
'Specify the height in pixels and calculate width based on aspect ratio.'
}
title={'Set Height'}
description={t('image.resize.setHeightDescription')}
title={t('image.resize.setHeight')}
/>
</Box>
)}
<TextFieldWithDesc
value={values.width}
onOwnChange={(val) => updateField('width', val)}
description={'Width (in pixels)'}
description={t('image.resize.widthDescription')}
disabled={
values.maintainAspectRatio &&
values.dimensionType === 'height'
@@ -131,7 +125,7 @@ export default function ResizeImage({ title }: ToolComponentProps) {
<TextFieldWithDesc
value={values.height}
onOwnChange={(val) => updateField('height', val)}
description={'Height (in pixels)'}
description={t('image.resize.heightDescription')}
disabled={
values.maintainAspectRatio &&
values.dimensionType === 'width'
@@ -148,15 +142,13 @@ export default function ResizeImage({ title }: ToolComponentProps) {
]
: [
{
title: 'Percentage',
title: t('image.resize.percentage'),
component: (
<Box>
<TextFieldWithDesc
value={values.percentage}
onOwnChange={(val) => updateField('percentage', val)}
description={
'Percentage of original size (e.g., 50 for half size, 200 for double size)'
}
description={t('image.resize.percentageDescription')}
inputProps={{
'data-testid': 'percentage-input',
type: 'number',
@@ -183,20 +175,19 @@ export default function ResizeImage({ title }: ToolComponentProps) {
value={input}
onChange={setInput}
accept={['image/jpeg', 'image/png', 'image/svg+xml', 'image/gif']}
title={'Input Image'}
title={t('image.resize.inputTitle')}
/>
}
resultComponent={
<ToolFileResult
title={'Resized Image'}
title={t('image.resize.resultTitle')}
value={result}
extension={input?.name.split('.').pop() || 'png'}
/>
}
toolInfo={{
title: 'Resize Image',
description:
'This tool allows you to resize JPG, PNG, SVG, or GIF images. You can resize by specifying dimensions in pixels or by percentage, with options to maintain the original aspect ratio.'
title: t('image.resize.toolInfo.title'),
description: t('image.resize.toolInfo.description')
}}
/>
);

View File

@@ -0,0 +1,40 @@
{
"resize": {
"title": "Resize Image",
"description": "Resize images to different dimensions.",
"inputTitle": "Input Image",
"resultTitle": "Resized Image",
"resizeMethod": "Resize Method",
"resizeByPixels": "Resize by Pixels",
"resizeByPixelsDescription": "Resize by specifying dimensions in pixels.",
"resizeByPercentage": "Resize by Percentage",
"resizeByPercentageDescription": "Resize by specifying a percentage of the original size.",
"dimensionType": "Dimension Type",
"maintainAspectRatio": "Maintain Aspect Ratio",
"maintainAspectRatioDescription": "Maintain the original aspect ratio of the image.",
"setWidth": "Set Width",
"setWidthDescription": "Specify the width in pixels and calculate height based on aspect ratio.",
"setHeight": "Set Height",
"setHeightDescription": "Specify the height in pixels and calculate width based on aspect ratio.",
"widthDescription": "Width (in pixels)",
"heightDescription": "Height (in pixels)",
"percentage": "Percentage",
"percentageDescription": "Percentage of original size (e.g., 50 for half size, 200 for double size)",
"toolInfo": {
"title": "Resize Image",
"description": "This tool allows you to resize JPG, PNG, SVG, or GIF images. You can resize by specifying dimensions in pixels or by percentage, with options to maintain the original aspect ratio."
}
},
"compress": {
"title": "Compress Image",
"description": "Reduce image file size while maintaining quality.",
"inputTitle": "Input image",
"resultTitle": "Compressed image"
},
"crop": {
"title": "Crop Image",
"description": "Crop images to remove unwanted areas.",
"inputTitle": "Input image",
"resultTitle": "Cropped image"
}
}

View File

@@ -4,11 +4,15 @@ import { lazy } from 'react';
export const tool = defineTool('json', {
name: 'Escape JSON',
path: 'escape-json',
icon: 'lets-icons:json-light',
icon: 'material-symbols:code',
description:
'Free online JSON escaper. Just load your JSON in the input field and it will automatically get escaped. In the tool options, you can optionally enable wrapping the escaped JSON in double quotes to get an escaped JSON string.',
shortDescription: 'Quickly escape special JSON characters.',
longDescription: `This tool converts special characters in JSON files and data structures into their escaped versions. Such special characters are, for example, double quotes, newline characters, backslashes, tabs, and many others. If these characters aren't escaped and appear in a raw JSON string without escaping, they can lead to errors in data parsing. The program turns them into safe versions by adding a backslash (\\) before the character, changing its interpretation. Additionally, you can enable the "Wrap Output in Quotes" checkbox in the options, which adds double quotes around the resulting escaped JSON data. This is useful when the escaped JSON data needs to be used as a string in other data structures or the JavaScript programming language. Json-abulous!`,
keywords: ['escape', 'json'],
component: lazy(() => import('./index'))
'Escape special characters in JSON strings. Convert JSON data to properly escaped format for safe transmission or storage.',
shortDescription: 'Escape special characters in JSON',
keywords: ['json', 'escape', 'characters', 'format'],
component: lazy(() => import('./index')),
i18n: {
name: 'json.escapeJson.name',
description: 'json.escapeJson.description',
shortDescription: 'json.escapeJson.shortDescription'
}
});

View File

@@ -0,0 +1,39 @@
{
"prettify": {
"title": "Prettify JSON",
"description": "Format JSON with proper indentation and spacing.",
"inputTitle": "Input JSON",
"resultTitle": "Prettified JSON",
"indentation": "Indentation",
"useSpaces": "Use Spaces",
"useSpacesDescription": "Indent output with spaces",
"useTabs": "Use Tabs",
"useTabsDescription": "Indent output with tabs.",
"toolInfo": {
"title": "Prettify JSON",
"description": "This tool allows you to format JSON data with proper indentation and spacing, making it more readable and easier to work with."
}
},
"minify": {
"title": "Minify JSON",
"description": "Remove all unnecessary whitespace from JSON.",
"inputTitle": "Input JSON",
"resultTitle": "Minified JSON",
"toolInfo": {
"title": "What Is JSON Minification?",
"description": "JSON minification is the process of removing all unnecessary whitespace characters from JSON data while maintaining its validity. This includes removing spaces, newlines, and indentation that aren't required for the JSON to be parsed correctly. Minification reduces the size of JSON data, making it more efficient for storage and transmission while keeping the exact same data structure and values."
}
},
"validateJson": {
"title": "Validate JSON",
"description": "Check if JSON is valid and well-formed.",
"inputTitle": "Input JSON",
"resultTitle": "Validation Result",
"validJson": "✅ Valid JSON",
"invalidJson": "❌ {{error}}",
"toolInfo": {
"title": "What is JSON Validation?",
"description": "JSON (JavaScript Object Notation) is a lightweight data-interchange format. JSON validation ensures that the structure of the data conforms to the JSON standard. A valid JSON object must have: - Property names enclosed in double quotes. - Properly balanced curly braces {}. - No trailing commas after the last key-value pair. - Proper nesting of objects and arrays. This tool checks the input JSON and provides feedback to help identify and fix common errors."
}
}
}

View File

@@ -2,11 +2,17 @@ import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('json', {
name: 'Convert JSON to XML',
name: 'JSON to XML',
path: 'json-to-xml',
icon: 'mdi-light:xml',
description: 'Convert JSON files to XML format with customizable options.',
shortDescription: 'Convert JSON data to XML format',
keywords: ['json', 'xml', 'convert', 'transform', 'parse'],
component: lazy(() => import('./index'))
icon: 'material-symbols:code',
description:
'Convert JSON data to XML format. Transform structured JSON objects into well-formed XML documents.',
shortDescription: 'Convert JSON to XML format',
keywords: ['json', 'xml', 'convert', 'transform'],
component: lazy(() => import('./index')),
i18n: {
name: 'json.jsonToXml.name',
description: 'json.jsonToXml.description',
shortDescription: 'json.jsonToXml.shortDescription'
}
});

View File

@@ -5,6 +5,7 @@ import ToolTextResult from '@components/result/ToolTextResult';
import { minifyJson } from './service';
import { CardExampleType } from '@components/examples/ToolExamples';
import { ToolComponentProps } from '@tools/defineTool';
import { useTranslation } from 'react-i18next';
type InitialValuesType = Record<string, never>;
@@ -47,6 +48,7 @@ const exampleCards: CardExampleType<InitialValuesType>[] = [
];
export default function MinifyJson({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -58,11 +60,15 @@ export default function MinifyJson({ title }: ToolComponentProps) {
<ToolContent
title={title}
inputComponent={
<ToolTextInput title="Input JSON" value={input} onChange={setInput} />
<ToolTextInput
title={t('json.minify.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult
title="Minified JSON"
title={t('json.minify.resultTitle')}
value={result}
extension={'json'}
/>
@@ -70,9 +76,8 @@ export default function MinifyJson({ title }: ToolComponentProps) {
initialValues={initialValues}
getGroups={null}
toolInfo={{
title: 'What Is JSON Minification?',
description:
"JSON minification is the process of removing all unnecessary whitespace characters from JSON data while maintaining its validity. This includes removing spaces, newlines, and indentation that aren't required for the JSON to be parsed correctly. Minification reduces the size of JSON data, making it more efficient for storage and transmission while keeping the exact same data structure and values."
title: t('json.minify.toolInfo.title'),
description: t('json.minify.toolInfo.description')
}}
exampleCards={exampleCards}
input={input}

View File

@@ -4,10 +4,15 @@ import { lazy } from 'react';
export const tool = defineTool('json', {
name: 'Minify JSON',
path: 'minify',
icon: 'lets-icons:json-light',
icon: 'material-symbols:code',
description:
'Minify your JSON by removing all unnecessary whitespace and formatting. This tool compresses JSON data to its smallest possible size while maintaining valid JSON structure.',
shortDescription: 'Quickly compress JSON file.',
keywords: ['minify', 'compress', 'minimize', 'json', 'compact'],
component: lazy(() => import('./index'))
'Minify JSON data by removing unnecessary whitespace and formatting. Reduce file size while maintaining data integrity.',
shortDescription: 'Minify JSON by removing whitespace',
keywords: ['json', 'minify', 'compress', 'whitespace'],
component: lazy(() => import('./index')),
i18n: {
name: 'json.minify.name',
description: 'json.minify.description',
shortDescription: 'json.minify.shortDescription'
}
});

View File

@@ -14,6 +14,7 @@ import RadioWithTextField from '@components/options/RadioWithTextField';
import SimpleRadio from '@components/options/SimpleRadio';
import { isNumber, updateNumberField } from '../../../../utils/string';
import ToolContent from '@components/ToolContent';
import { useTranslation } from 'react-i18next';
type InitialValuesType = {
indentationType: 'tab' | 'space';
@@ -115,6 +116,7 @@ const exampleCards: CardExampleType<InitialValuesType>[] = [
];
export default function PrettifyJson({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -128,11 +130,15 @@ export default function PrettifyJson({ title }: ToolComponentProps) {
title={title}
input={input}
inputComponent={
<ToolTextInput title={'Input JSON'} value={input} onChange={setInput} />
<ToolTextInput
title={t('json.prettify.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult
title={'Pretty JSON'}
title={t('json.prettify.resultTitle')}
value={result}
extension={'json'}
/>
@@ -140,14 +146,14 @@ export default function PrettifyJson({ title }: ToolComponentProps) {
initialValues={initialValues}
getGroups={({ values, updateField }) => [
{
title: 'Indentation',
title: t('json.prettify.indentation'),
component: (
<Box>
<RadioWithTextField
checked={values.indentationType === 'space'}
title={'Use Spaces'}
title={t('json.prettify.useSpaces')}
fieldName={'indentationType'}
description={'Indent output with spaces'}
description={t('json.prettify.useSpacesDescription')}
value={values.spacesCount.toString()}
onRadioClick={() => updateField('indentationType', 'space')}
onTextChange={(val) =>
@@ -157,8 +163,8 @@ export default function PrettifyJson({ title }: ToolComponentProps) {
<SimpleRadio
onClick={() => updateField('indentationType', 'tab')}
checked={values.indentationType === 'tab'}
description={'Indent output with tabs.'}
title={'Use Tabs'}
description={t('json.prettify.useTabsDescription')}
title={t('json.prettify.useTabs')}
/>
</Box>
)
@@ -168,9 +174,8 @@ export default function PrettifyJson({ title }: ToolComponentProps) {
setInput={setInput}
exampleCards={exampleCards}
toolInfo={{
title: 'What Is a JSON Prettifier?',
description:
'This tool adds consistent formatting to the data in JavaScript Object Notation (JSON) format. This transformation makes the JSON code more readable, making it easier to understand and edit. The program parses the JSON data structure into tokens and then reformats them by adding indentation and line breaks. If the data is hierarchial, then it adds indentation at the beginning of lines to visually show the depth of the JSON and adds newlines to break long single-line JSON arrays into multiple shorter, more readable ones. Additionally, this utility can remove unnecessary spaces and tabs from your JSON code (especially leading and trailing whitespaces), making it more compact. You can choose the line indentation method in the options: indent with spaces or indent with tabs. When using spaces, you can also specify how many spaces to use for each indentation level (usually 2 or 4 spaces). '
title: t('json.prettify.toolInfo.title'),
description: t('json.prettify.toolInfo.description')
}}
/>
);

View File

@@ -4,10 +4,15 @@ import { lazy } from 'react';
export const tool = defineTool('json', {
name: 'Prettify JSON',
path: 'prettify',
icon: 'lets-icons:json-light',
icon: 'material-symbols:code',
description:
"Just load your JSON in the input field and it will automatically get prettified. In the tool options, you can choose whether to use spaces or tabs for indentation and if you're using spaces, you can specify the number of spaces to add per indentation level.",
shortDescription: 'Quickly beautify a JSON data structure.',
keywords: ['prettify'],
component: lazy(() => import('./index'))
'Format and beautify JSON data with proper indentation and spacing. Make JSON files more readable and organized.',
shortDescription: 'Format and beautify JSON code',
keywords: ['json', 'prettify', 'format', 'beautify'],
component: lazy(() => import('./index')),
i18n: {
name: 'json.prettify.name',
description: 'json.prettify.description',
shortDescription: 'json.prettify.shortDescription'
}
});

View File

@@ -4,18 +4,15 @@ import { lazy } from 'react';
export const tool = defineTool('json', {
name: 'Stringify JSON',
path: 'stringify',
icon: 'ant-design:field-string-outlined',
icon: 'material-symbols:code',
description:
'Convert JavaScript objects and arrays into their JSON string representation. Options include custom indentation and HTML character escaping for web-safe JSON strings.',
shortDescription: 'Convert JavaScript objects to JSON strings',
keywords: [
'stringify',
'serialize',
'convert',
'object',
'array',
'json',
'string'
],
component: lazy(() => import('./index'))
'Convert JavaScript objects to JSON string format. Serialize data structures into JSON strings for storage or transmission.',
shortDescription: 'Convert objects to JSON string',
keywords: ['json', 'stringify', 'serialize', 'convert'],
component: lazy(() => import('./index')),
i18n: {
name: 'json.stringify.name',
description: 'json.stringify.description',
shortDescription: 'json.stringify.shortDescription'
}
});

View File

@@ -2,14 +2,17 @@ import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('json', {
name: 'Convert TSV to JSON',
name: 'TSV to JSON',
path: 'tsv-to-json',
icon: 'material-symbols:tsv-rounded',
icon: 'material-symbols:code',
description:
'Convert TSV files to JSON format with customizable options for delimiters, quotes, and output formatting. Support for headers, comments, and dynamic type conversion.',
shortDescription: 'Convert TSV data to JSON format.',
longDescription:
'This tool allows you to convert TSV (Tab-Separated Values) files into JSON format. You can customize the conversion process by specifying delimiters, quote characters, and whether to use headers. It also supports dynamic type conversion for values, handling comments, and skipping empty lines. The output can be formatted with indentation or minified as needed.',
keywords: ['tsv', 'json', 'convert', 'transform', 'parse'],
component: lazy(() => import('./index'))
'Convert TSV (Tab-Separated Values) data to JSON format. Transform tabular data into structured JSON objects.',
shortDescription: 'Convert TSV to JSON format',
keywords: ['tsv', 'json', 'convert', 'tabular'],
component: lazy(() => import('./index')),
i18n: {
name: 'json.tsvToJson.name',
description: 'json.tsvToJson.description',
shortDescription: 'json.tsvToJson.shortDescription'
}
});

View File

@@ -5,6 +5,7 @@ import { CardExampleType } from '@components/examples/ToolExamples';
import { validateJson } from './service';
import { ToolComponentProps } from '@tools/defineTool';
import ToolContent from '@components/ToolContent';
import { useTranslation } from 'react-i18next';
const exampleCards: CardExampleType<{}>[] = [
{
@@ -46,6 +47,7 @@ const exampleCards: CardExampleType<{}>[] = [
];
export default function ValidateJson({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -53,9 +55,9 @@ export default function ValidateJson({ title }: ToolComponentProps) {
const { valid, error } = validateJson(input);
if (valid) {
setResult('✅ Valid JSON');
setResult(t('json.validateJson.validJson'));
} else {
setResult(`${error}`);
setResult(t('json.validateJson.invalidJson', { error }));
}
};
@@ -63,25 +65,23 @@ export default function ValidateJson({ title }: ToolComponentProps) {
<ToolContent
title={title}
inputComponent={
<ToolTextInput title="Input JSON" value={input} onChange={setInput} />
<ToolTextInput
title={t('json.validateJson.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult title="Validation Result" value={result} />
<ToolTextResult
title={t('json.validateJson.resultTitle')}
value={result}
/>
}
initialValues={{}}
getGroups={null}
toolInfo={{
title: 'What is JSON Validation?',
description: `
JSON (JavaScript Object Notation) is a lightweight data-interchange format.
JSON validation ensures that the structure of the data conforms to the JSON standard.
A valid JSON object must have:
- Property names enclosed in double quotes.
- Properly balanced curly braces {}.
- No trailing commas after the last key-value pair.
- Proper nesting of objects and arrays.
This tool checks the input JSON and provides feedback to help identify and fix common errors.
`
title: t('json.validateJson.toolInfo.title'),
description: t('json.validateJson.toolInfo.description')
}}
exampleCards={exampleCards}
input={input}

View File

@@ -3,11 +3,16 @@ import { lazy } from 'react';
export const tool = defineTool('json', {
name: 'Validate JSON',
path: 'validateJson',
icon: 'lets-icons:json-light',
path: 'validate-json',
icon: 'material-symbols:check-circle',
description:
'Validate JSON data and identify formatting issues such as missing quotes, trailing commas, and incorrect brackets.',
shortDescription: 'Quickly validate a JSON data structure.',
keywords: ['validate', 'json', 'syntax'],
component: lazy(() => import('./index'))
'Validate JSON code for syntax errors and proper structure. Check if JSON documents follow correct formatting rules.',
shortDescription: 'Validate JSON code for errors',
keywords: ['json', 'validate', 'check', 'syntax', 'errors'],
component: lazy(() => import('./index')),
i18n: {
name: 'json.validateJson.name',
description: 'json.validateJson.description',
shortDescription: 'json.validateJson.shortDescription'
}
});

View File

@@ -11,6 +11,7 @@ import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import SimpleRadio from '@components/options/SimpleRadio';
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
import * as Yup from 'yup';
import { useTranslation } from 'react-i18next';
interface InitialValuesType {
splitOperatorType: SplitOperatorType;
@@ -101,6 +102,7 @@ const exampleCards: CardExampleType<InitialValuesType>[] = [
];
export default function Duplicate({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -134,55 +136,53 @@ export default function Duplicate({ title }: ToolComponentProps) {
updateField
}) => [
{
title: 'Split Options',
title: t('list.duplicate.splitOptions'),
component: (
<Box>
<SimpleRadio
onClick={() => updateField('splitOperatorType', 'symbol')}
checked={values.splitOperatorType === 'symbol'}
title={'Split by Symbol'}
title={t('list.duplicate.splitBySymbol')}
/>
<SimpleRadio
onClick={() => updateField('splitOperatorType', 'regex')}
checked={values.splitOperatorType === 'regex'}
title={'Split by Regular Expression'}
title={t('list.duplicate.splitByRegex')}
/>
<TextFieldWithDesc
value={values.splitSeparator}
onOwnChange={(val) => updateField('splitSeparator', val)}
description={'Separator to split the list'}
description={t('list.duplicate.splitSeparatorDescription')}
/>
<TextFieldWithDesc
value={values.joinSeparator}
onOwnChange={(val) => updateField('joinSeparator', val)}
description={'Separator to join the duplicated list'}
description={t('list.duplicate.joinSeparatorDescription')}
/>
</Box>
)
},
{
title: 'Duplication Options',
title: t('list.duplicate.duplicationOptions'),
component: (
<Box>
<TextFieldWithDesc
value={values.copy}
onOwnChange={(val) => updateField('copy', val)}
description={'Number of copies (can be fractional)'}
description={t('list.duplicate.copyDescription')}
type="number"
/>
<CheckboxWithDesc
title={'Concatenate'}
title={t('list.duplicate.concatenate')}
checked={values.concatenate}
onChange={(checked) => updateField('concatenate', checked)}
description={
'Concatenate copies (if unchecked, items will be interweaved)'
}
description={t('list.duplicate.concatenateDescription')}
/>
<CheckboxWithDesc
title={'Reverse'}
title={t('list.duplicate.reverse')}
checked={values.reverse}
onChange={(checked) => updateField('reverse', checked)}
description={'Reverse the duplicated items'}
description={t('list.duplicate.reverseDescription')}
/>
</Box>
)
@@ -193,18 +193,24 @@ export default function Duplicate({ title }: ToolComponentProps) {
<ToolContent
title={title}
inputComponent={
<ToolTextInput title="Input List" value={input} onChange={setInput} />
<ToolTextInput
title={t('list.duplicate.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult title="Duplicated List" value={result} />
<ToolTextResult
title={t('list.duplicate.resultTitle')}
value={result}
/>
}
initialValues={initialValues}
getGroups={getGroups}
validationSchema={validationSchema}
toolInfo={{
title: 'List Duplication',
description:
"This tool allows you to duplicate items in a list. You can specify the number of copies (including fractional values), control whether items are concatenated or interweaved, and even reverse the duplicated items. It's useful for creating repeated patterns, generating test data, or expanding lists with predictable content."
title: t('list.duplicate.toolInfo.title'),
description: t('list.duplicate.toolInfo.description')
}}
exampleCards={exampleCards}
input={input}

View File

@@ -5,10 +5,15 @@ import { lazy } from 'react';
export const tool = defineTool('list', {
name: 'Duplicate',
path: 'duplicate',
icon: 'mdi:content-duplicate',
icon: 'material-symbols-light:content-copy',
description:
'A tool to duplicate each item in a list a specified number of times. Perfect for creating repeated patterns, test data, or expanding datasets.',
shortDescription: 'Repeat items in a list multiple times.',
"World's simplest browser-based utility for duplicating list items. Input your list and specify duplication criteria to create copies of items. Perfect for data expansion, testing, or creating repeated patterns.",
shortDescription: 'Duplicate list items with specified criteria',
keywords: ['duplicate'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'list.duplicate.name',
description: 'list.duplicate.description',
shortDescription: 'list.duplicate.shortDescription'
}
});

View File

@@ -5,10 +5,15 @@ import { lazy } from 'react';
export const tool = defineTool('list', {
name: 'Find most popular',
path: 'find-most-popular',
icon: 'material-symbols-light:query-stats',
icon: 'material-symbols-light:trending-up',
description:
'A tool to identify and count the most frequently occurring items in a list. Useful for data analysis, finding trends, or identifying common elements.',
shortDescription: 'Find most common items in a list.',
"World's simplest browser-based utility for finding the most popular items in a list. Input your list and instantly get the items that appear most frequently. Perfect for data analysis, trend identification, or finding common elements.",
shortDescription: 'Find most frequently occurring items',
keywords: ['find', 'most', 'popular'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'list.findMostPopular.name',
description: 'list.findMostPopular.description',
shortDescription: 'list.findMostPopular.shortDescription'
}
});

View File

@@ -7,6 +7,7 @@ import { findUniqueCompute, SplitOperatorType } from './service';
import SimpleRadio from '@components/options/SimpleRadio';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
import { useTranslation } from 'react-i18next';
const initialValues = {
splitOperatorType: 'symbol' as SplitOperatorType,
@@ -35,6 +36,7 @@ const splitOperators: {
];
export default function FindUnique() {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
const compute = (optionsValues: typeof initialValues, input: any) => {
@@ -64,18 +66,27 @@ export default function FindUnique() {
return (
<ToolContent
title="Find Unique"
title={t('list.findUnique.title')}
initialValues={initialValues}
compute={compute}
input={input}
setInput={setInput}
inputComponent={
<ToolTextInput title={'Input list'} value={input} onChange={setInput} />
<ToolTextInput
title={t('list.findUnique.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult
title={t('list.findUnique.resultTitle')}
value={result}
/>
}
resultComponent={<ToolTextResult title={'Unique items'} value={result} />}
getGroups={({ values, updateField }) => [
{
title: 'Input List Delimiter',
title: t('list.findUnique.inputListDelimiter'),
component: (
<Box>
{splitOperators.map(({ title, description, type }) => (
@@ -88,7 +99,7 @@ export default function FindUnique() {
/>
))}
<TextFieldWithDesc
description={'Set a delimiting symbol or regular expression.'}
description={t('list.findUnique.delimiterDescription')}
value={values.splitSeparator}
onOwnChange={(val) => updateField('splitSeparator', val)}
/>
@@ -96,7 +107,7 @@ export default function FindUnique() {
)
},
{
title: 'Output List Delimiter',
title: t('list.findUnique.outputListDelimiter'),
component: (
<Box>
<TextFieldWithDesc
@@ -104,18 +115,14 @@ export default function FindUnique() {
onOwnChange={(value) => updateField('joinSeparator', value)}
/>
<CheckboxWithDesc
title={'Trim top list items'}
description={
'Remove leading and trailing spaces before comparing items'
}
title={t('list.findUnique.trimItems')}
description={t('list.findUnique.trimItemsDescription')}
checked={values.trimItems}
onChange={(value) => updateField('trimItems', value)}
/>
<CheckboxWithDesc
title={'Skip empty items'}
description={
"Don't include the empty list items in the output."
}
title={t('list.findUnique.skipEmptyItems')}
description={t('list.findUnique.skipEmptyItemsDescription')}
checked={values.deleteEmptyItems}
onChange={(value) => updateField('deleteEmptyItems', value)}
/>
@@ -123,22 +130,20 @@ export default function FindUnique() {
)
},
{
title: 'Unique Item Options',
title: t('list.findUnique.uniqueItemOptions'),
component: (
<Box>
<CheckboxWithDesc
title={'Find Absolutely Unique Items'}
description={
'Display only those items of the list that exist in a single copy.'
}
title={t('list.findUnique.findAbsolutelyUniqueItems')}
description={t(
'list.findUnique.findAbsolutelyUniqueItemsDescription'
)}
checked={values.absolutelyUnique}
onChange={(value) => updateField('absolutelyUnique', value)}
/>
<CheckboxWithDesc
title={'Case Sensitive Items'}
description={
'Output items with different case as unique elements in the list.'
}
title={t('list.findUnique.caseSensitiveItems')}
description={t('list.findUnique.caseSensitiveItemsDescription')}
checked={values.caseSensitive}
onChange={(value) => updateField('caseSensitive', value)}
/>

View File

@@ -4,9 +4,15 @@ import { lazy } from 'react';
export const tool = defineTool('list', {
name: 'Find unique',
path: 'find-unique',
icon: 'mynaui:one',
description: "World's simplest browser-based utility for finding unique items in a list. Just input your list with any separator, and it will automatically identify and extract unique items. Perfect for removing duplicates, finding distinct values, or analyzing data uniqueness. You can customize the input/output separators and choose whether to preserve the original order.",
icon: 'material-symbols-light:search',
description:
"World's simplest browser-based utility for finding unique items in a list. Input your list and instantly get all unique values with duplicates removed. Perfect for data cleaning, deduplication, or finding distinct elements.",
shortDescription: 'Find unique items in a list',
keywords: ['find', 'unique'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'list.findUnique.name',
description: 'list.findUnique.description',
shortDescription: 'list.findUnique.shortDescription'
}
});

View File

@@ -9,6 +9,7 @@ import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
import { formatNumber } from '../../../../utils/number';
import ToolContent from '@components/ToolContent';
import { ToolComponentProps } from '@tools/defineTool';
import { useTranslation } from 'react-i18next';
const initialValues = {
splitOperatorType: 'symbol' as SplitOperatorType,
@@ -40,6 +41,7 @@ const splitOperators: {
];
export default function FindUnique({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
const compute = (optionsValues: typeof initialValues, input: any) => {
@@ -78,28 +80,34 @@ export default function FindUnique({ title }: ToolComponentProps) {
title={title}
input={input}
inputComponent={
<ToolTextInput title={'Input list'} value={input} onChange={setInput} />
<ToolTextInput
title={t('list.group.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult title={'Grouped items'} value={result} />
<ToolTextResult title={t('list.group.resultTitle')} value={result} />
}
initialValues={initialValues}
getGroups={({ values, updateField }) => [
{
title: 'Input Item Separator',
title: t('list.group.inputItemSeparator'),
component: (
<Box>
{splitOperators.map(({ title, description, type }) => (
<SimpleRadio
key={type}
onClick={() => updateField('splitOperatorType', type)}
title={title}
description={description}
title={t(`list.group.splitOperators.${type}.title`)}
description={t(
`list.group.splitOperators.${type}.description`
)}
checked={values.splitOperatorType === type}
/>
))}
<TextFieldWithDesc
description={'Set a delimiting symbol or regular expression.'}
description={t('list.group.splitSeparatorDescription')}
value={values.splitSeparator}
onOwnChange={(val) => updateField('splitSeparator', val)}
/>
@@ -107,12 +115,12 @@ export default function FindUnique({ title }: ToolComponentProps) {
)
},
{
title: 'Group Size and Separators',
title: t('list.group.groupSizeAndSeparators'),
component: (
<Box>
<TextFieldWithDesc
value={values.groupNumber}
description={'Number of items in a group'}
description={t('list.group.groupNumberDescription')}
type={'number'}
onOwnChange={(value) =>
updateField('groupNumber', formatNumber(value, 1))
@@ -120,52 +128,46 @@ export default function FindUnique({ title }: ToolComponentProps) {
/>
<TextFieldWithDesc
value={values.itemSeparator}
description={'Item separator character'}
description={t('list.group.itemSeparatorDescription')}
onOwnChange={(value) => updateField('itemSeparator', value)}
/>
<TextFieldWithDesc
value={values.groupSeparator}
description={'Group separator character'}
description={t('list.group.groupSeparatorDescription')}
onOwnChange={(value) => updateField('groupSeparator', value)}
/>
<TextFieldWithDesc
value={values.leftWrap}
description={"Group's left wrap symbol."}
description={t('list.group.leftWrapDescription')}
onOwnChange={(value) => updateField('leftWrap', value)}
/>
<TextFieldWithDesc
value={values.rightWrap}
description={"Group's right wrap symbol."}
description={t('list.group.rightWrapDescription')}
onOwnChange={(value) => updateField('rightWrap', value)}
/>
</Box>
)
},
{
title: 'Empty Items and Padding',
title: t('list.group.emptyItemsAndPadding'),
component: (
<Box>
<CheckboxWithDesc
title={'Delete Empty Items'}
description={
"Ignore empty items and don't include them in the groups."
}
title={t('list.group.deleteEmptyItems')}
description={t('list.group.deleteEmptyItemsDescription')}
checked={values.deleteEmptyItems}
onChange={(value) => updateField('deleteEmptyItems', value)}
/>
<CheckboxWithDesc
title={'Pad Non-full Groups'}
description={
'Fill non-full groups with a custom item (enter below).'
}
title={t('list.group.padNonFullGroups')}
description={t('list.group.padNonFullGroupsDescription')}
checked={values.padNonFullGroup}
onChange={(value) => updateField('padNonFullGroup', value)}
/>
<TextFieldWithDesc
value={values.paddingChar}
description={
'Use this character or item to pad non-full groups.'
}
description={t('list.group.paddingCharDescription')}
onOwnChange={(value) => updateField('paddingChar', value)}
/>
</Box>

View File

@@ -5,8 +5,14 @@ export const tool = defineTool('list', {
name: 'Group',
path: 'group',
icon: 'pajamas:group',
description: "World's simplest browser-based utility for grouping list items. Input your list and specify grouping criteria to organize items into logical groups. Perfect for categorizing data, organizing information, or creating structured lists. Supports custom separators and various grouping options.",
description:
"World's simplest browser-based utility for grouping list items. Input your list and specify grouping criteria to organize items into logical groups. Perfect for categorizing data, organizing information, or creating structured lists. Supports custom separators and various grouping options.",
shortDescription: 'Group list items by common properties',
keywords: ['group'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'list.group.name',
description: 'list.group.description',
shortDescription: 'list.group.shortDescription'
}
});

View File

@@ -0,0 +1,115 @@
{
"group": {
"title": "Group List",
"description": "Group list items by common properties.",
"inputTitle": "Input list",
"resultTitle": "Grouped list",
"groupingOptions": "Grouping Options",
"groupByDescription": "Choose how to group the list items",
"groupByValue": "Group by Value",
"groupByValueDescription": "Group items that have the same value",
"groupByLength": "Group by Length",
"groupByLengthDescription": "Group items by their character length",
"groupByFirstChar": "Group by First Character",
"groupByFirstCharDescription": "Group items by their first character",
"groupByLastChar": "Group by Last Character",
"groupByLastCharDescription": "Group items by their last character",
"toolInfo": {
"title": "Group List",
"description": "This tool allows you to group list items by various criteria such as value, length, or character position. It's useful for organizing and categorizing data."
}
},
"reverse": {
"title": "Reverse List",
"description": "Reverse the order of items in a list.",
"inputTitle": "Input list",
"resultTitle": "Reversed list",
"reverseOptions": "Reverse Options",
"reverseEachLine": "Reverse Each Line",
"reverseEachLineDescription": "Reverse each line separately instead of the entire list",
"toolInfo": {
"title": "Reverse List",
"description": "This tool allows you to reverse the order of items in a list. You can reverse the entire list or each line separately."
}
},
"sort": {
"title": "Sort List",
"description": "Sort list items in ascending or descending order.",
"inputTitle": "Input list",
"resultTitle": "Sorted list",
"sortOptions": "Sort Options",
"sortOrder": "Sort Order",
"ascending": "Ascending",
"descending": "Descending",
"sortType": "Sort Type",
"alphabetical": "Alphabetical",
"numerical": "Numerical",
"natural": "Natural",
"toolInfo": {
"title": "Sort List",
"description": "This tool allows you to sort list items in various orders and types. You can sort alphabetically, numerically, or using natural sorting."
}
},
"duplicate": {
"title": "Find Duplicates",
"description": "Find and remove duplicate items from a list.",
"inputTitle": "Input list",
"resultTitle": "Unique list",
"duplicateOptions": "Duplicate Options",
"removeDuplicates": "Remove Duplicates",
"removeDuplicatesDescription": "Remove duplicate items from the list",
"showDuplicates": "Show Duplicates",
"showDuplicatesDescription": "Show only the duplicate items",
"toolInfo": {
"title": "Find Duplicates",
"description": "This tool allows you to find and handle duplicate items in a list. You can remove duplicates or show only the duplicate items."
}
},
"findUnique": {
"title": "Find Unique",
"description": "Find unique items in a list.",
"inputTitle": "Input list",
"resultTitle": "Unique items",
"uniqueOptions": "Unique Options",
"showUnique": "Show Unique",
"showUniqueDescription": "Show only unique items",
"showDuplicates": "Show Duplicates",
"showDuplicatesDescription": "Show only duplicate items",
"toolInfo": {
"title": "Find Unique",
"description": "This tool allows you to find unique items in a list. You can show unique items or duplicate items."
}
},
"shuffle": {
"title": "Shuffle List",
"description": "Randomly shuffle the order of list items.",
"inputTitle": "Input list",
"resultTitle": "Shuffled list",
"shuffleOptions": "Shuffle Options",
"shuffleEachLine": "Shuffle Each Line",
"shuffleEachLineDescription": "Shuffle each line separately instead of the entire list",
"toolInfo": {
"title": "Shuffle List",
"description": "This tool allows you to randomly shuffle the order of items in a list. You can shuffle the entire list or each line separately."
}
},
"wrap": {
"title": "Wrap List",
"description": "Add text before and after each list item.",
"inputTitle": "Input List",
"resultTitle": "Wrapped List",
"splitOptions": "Split Options",
"splitBySymbol": "Split by Symbol",
"splitByRegex": "Split by Regular Expression",
"splitSeparatorDescription": "Separator to split the list",
"joinSeparatorDescription": "Separator to join the wrapped list",
"removeEmptyItems": "Remove empty items",
"wrapOptions": "Wrap Options",
"leftTextDescription": "Text to add before each item",
"rightTextDescription": "Text to add after each item",
"toolInfo": {
"title": "List Wrapping",
"description": "This tool allows you to add text before and after each item in a list. You can specify different text for the left and right sides, and control how the list is processed. It's useful for adding quotes, brackets, or other formatting to list items, preparing data for different formats, or creating structured text."
}
}
}

View File

@@ -9,6 +9,7 @@ import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import { CardExampleType } from '@components/examples/ToolExamples';
import { ToolComponentProps } from '@tools/defineTool';
import ToolContent from '@components/ToolContent';
import { useTranslation } from 'react-i18next';
const initialValues = {
splitOperatorType: 'symbol' as SplitOperatorType,
@@ -111,6 +112,7 @@ argument`,
];
export default function Reverse({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -119,15 +121,15 @@ export default function Reverse({ title }: ToolComponentProps) {
updateField
}) => [
{
title: 'Splitter Mode',
title: t('list.reverse.splitterMode'),
component: (
<Box>
{splitOperators.map(({ title, description, type }) => (
<SimpleRadio
key={type}
onClick={() => updateField('splitOperatorType', type)}
title={title}
description={description}
title={t(`list.reverse.splitOperators.${type}.title`)}
description={t(`list.reverse.splitOperators.${type}.description`)}
checked={values.splitOperatorType === type}
/>
))}
@@ -135,11 +137,11 @@ export default function Reverse({ title }: ToolComponentProps) {
)
},
{
title: 'Item Separator',
title: t('list.reverse.itemSeparator'),
component: (
<Box>
<TextFieldWithDesc
description={'Set a delimiting symbol or regular expression.'}
description={t('list.reverse.itemSeparatorDescription')}
value={values.splitSeparator}
onOwnChange={(val) => updateField('splitSeparator', val)}
/>
@@ -147,11 +149,11 @@ export default function Reverse({ title }: ToolComponentProps) {
)
},
{
title: 'Output List Options',
title: t('list.reverse.outputListOptions'),
component: (
<Box>
<TextFieldWithDesc
description={'Output list item separator.'}
description={t('list.reverse.outputSeparatorDescription')}
value={values.joinSeparator}
onOwnChange={(val) => updateField('joinSeparator', val)}
/>
@@ -176,15 +178,18 @@ export default function Reverse({ title }: ToolComponentProps) {
input={input}
setInput={setInput}
inputComponent={
<ToolTextInput title={'Input list'} value={input} onChange={setInput} />
<ToolTextInput
title={t('list.reverse.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult title={'Reversed list'} value={result} />
<ToolTextResult title={t('list.reverse.resultTitle')} value={result} />
}
toolInfo={{
title: 'What Is a List Reverser?',
description:
'With this utility, you can reverse the order of items in a list. The utility first splits the input list into individual items and then iterates through them from the last item to the first item, printing each item to the output during the iteration. The input list may contain anything that can be represented as textual data, which includes digits, numbers, strings, words, sentences, etc. The input item separator can also be a regular expression. For example, the regex /[;,]/ will allow you to use items that are either comma- or semicolon-separated. The input and output list items delimiters can be customized in the options. By default, both input and output lists are comma-separated. Listabulous!'
title: t('list.reverse.toolInfo.title'),
description: t('list.reverse.toolInfo.description')
}}
exampleCards={exampleCards}
/>

View File

@@ -6,8 +6,14 @@ export const tool = defineTool('list', {
name: 'Reverse',
path: 'reverse',
icon: 'proicons:reverse',
description: 'This is a super simple browser-based application prints all list items in reverse. The input items can be separated by any symbol and you can also change the separator of the reversed list items.',
description:
'This is a super simple browser-based application prints all list items in reverse. The input items can be separated by any symbol and you can also change the separator of the reversed list items.',
shortDescription: 'Quickly reverse a list',
keywords: ['reverse'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'list.reverse.name',
description: 'list.reverse.description',
shortDescription: 'list.reverse.shortDescription'
}
});

View File

@@ -7,8 +7,13 @@ export const tool = defineTool('list', {
path: 'rotate',
icon: 'material-symbols-light:rotate-right',
description:
'A tool to rotate items in a list by a specified number of positions. Shift elements left or right while maintaining their relative order.',
shortDescription: 'Shift list items by position.',
"World's simplest browser-based utility for rotating list items. Input your list and specify rotation amount to shift items by a specified number of positions. Perfect for data manipulation, circular shifts, or reordering lists.",
shortDescription: 'Rotate list items by specified positions',
keywords: ['rotate'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'list.rotate.name',
description: 'list.rotate.description',
shortDescription: 'list.rotate.shortDescription'
}
});

View File

@@ -7,6 +7,7 @@ import { shuffleList, SplitOperatorType } from './service';
import SimpleRadio from '@components/options/SimpleRadio';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import { isNumber } from '@utils/string';
import { useTranslation } from 'react-i18next';
const initialValues = {
splitOperatorType: 'symbol' as SplitOperatorType,
@@ -32,6 +33,7 @@ const splitOperators: {
];
export default function Shuffle() {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
const compute = (optionsValues: typeof initialValues, input: any) => {
@@ -51,20 +53,24 @@ export default function Shuffle() {
return (
<ToolContent
title="Shuffle"
title={t('list.shuffle.title')}
initialValues={initialValues}
compute={compute}
input={input}
setInput={setInput}
inputComponent={
<ToolTextInput title={'Input list'} value={input} onChange={setInput} />
<ToolTextInput
title={t('list.shuffle.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult title={'Shuffled list'} value={result} />
<ToolTextResult title={t('list.shuffle.resultTitle')} value={result} />
}
getGroups={({ values, updateField }) => [
{
title: 'Input list separator',
title: t('list.shuffle.inputListSeparator'),
component: (
<Box>
{splitOperators.map(({ title, description, type }) => (
@@ -77,7 +83,7 @@ export default function Shuffle() {
/>
))}
<TextFieldWithDesc
description={'Set a delimiting symbol or regular expression.'}
description={t('list.shuffle.delimiterDescription')}
value={values.splitSeparator}
onOwnChange={(val) => updateField('splitSeparator', val)}
/>
@@ -85,11 +91,11 @@ export default function Shuffle() {
)
},
{
title: 'Shuffled List Length',
title: t('list.shuffle.shuffledListLength'),
component: (
<Box>
<TextFieldWithDesc
description={'Output this many random items'}
description={t('list.shuffle.outputLengthDescription')}
value={values.length}
onOwnChange={(val) => updateField('length', val)}
/>
@@ -97,13 +103,13 @@ export default function Shuffle() {
)
},
{
title: 'Shuffled List Separator',
title: t('list.shuffle.shuffledListSeparator'),
component: (
<Box>
<TextFieldWithDesc
value={values.joinSeparator}
onOwnChange={(value) => updateField('joinSeparator', value)}
description={'Use this separator in the randomized list.'}
description={t('list.shuffle.joinSeparatorDescription')}
/>
</Box>
)

View File

@@ -7,8 +7,13 @@ export const tool = defineTool('list', {
path: 'shuffle',
icon: 'material-symbols-light:shuffle',
description:
'A tool to randomly reorder items in a list. Perfect for randomizing data, creating random selections, or generating random sequences.',
shortDescription: 'Randomly reorder list items.',
"World's simplest browser-based utility for shuffling list items. Input your list and instantly get a randomized version with items in random order. Perfect for creating variety, testing randomness, or mixing up ordered data.",
shortDescription: 'Randomize the order of list items',
keywords: ['shuffle'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'list.shuffle.name',
description: 'list.shuffle.description',
shortDescription: 'list.shuffle.shortDescription'
}
});

View File

@@ -9,6 +9,7 @@ import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
import SelectWithDesc from '@components/options/SelectWithDesc';
import ToolContent from '@components/ToolContent';
import { ToolComponentProps } from '@tools/defineTool';
import { useTranslation } from 'react-i18next';
const initialValues = {
splitSeparatorType: 'symbol' as SplitOperatorType,
@@ -37,6 +38,7 @@ const splitOperators: {
];
export default function SplitText({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
const compute = (optionsValues: typeof initialValues, input: any) => {
@@ -69,26 +71,34 @@ export default function SplitText({ title }: ToolComponentProps) {
title={title}
input={input}
inputComponent={
<ToolTextInput title={'Input list'} value={input} onChange={setInput} />
<ToolTextInput
title={t('list.sort.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult title={t('list.sort.resultTitle')} value={result} />
}
resultComponent={<ToolTextResult title={'Sorted list'} value={result} />}
initialValues={initialValues}
getGroups={({ values, updateField }) => [
{
title: 'Input item separator',
title: t('list.sort.inputItemSeparator'),
component: (
<Box>
{splitOperators.map(({ title, description, type }) => (
<SimpleRadio
key={type}
onClick={() => updateField('splitSeparatorType', type)}
title={title}
description={description}
title={t(`list.sort.splitOperators.${type}.title`)}
description={t(
`list.sort.splitOperators.${type}.description`
)}
checked={values.splitSeparatorType === type}
/>
))}
<TextFieldWithDesc
description={'Set a delimiting symbol or regular expression.'}
description={t('list.sort.splitSeparatorDescription')}
value={values.splitSeparator}
onOwnChange={(val) => updateField('splitSeparator', val)}
/>
@@ -96,35 +106,45 @@ export default function SplitText({ title }: ToolComponentProps) {
)
},
{
title: 'Sort method',
title: t('list.sort.sortMethod'),
component: (
<Box>
<SelectWithDesc
selected={values.sortingMethod}
options={[
{ label: 'Sort Alphabetically', value: 'alphabetic' },
{ label: 'Sort Numerically', value: 'numeric' },
{ label: 'Sort by Length', value: 'length' }
{
label: t('list.sort.sortOptions.alphabetic'),
value: 'alphabetic'
},
{
label: t('list.sort.sortOptions.numeric'),
value: 'numeric'
},
{ label: t('list.sort.sortOptions.length'), value: 'length' }
]}
onChange={(value) => updateField('sortingMethod', value)}
description={'Select a sorting method.'}
description={t('list.sort.sortMethodDescription')}
/>
<SelectWithDesc
selected={values.increasing}
options={[
{ label: 'Increasing order', value: true },
{ label: 'Decreasing order', value: false }
{
label: t('list.sort.orderOptions.increasing'),
value: true
},
{
label: t('list.sort.orderOptions.decreasing'),
value: false
}
]}
onChange={(value) => {
updateField('increasing', value);
}}
description={'Select a sorting order.'}
description={t('list.sort.orderDescription')}
/>
<CheckboxWithDesc
title={'Case Sensitive Sort'}
description={
'Sort uppercase and lowercase items separately. Capital letters precede lowercase letters in an ascending list. (Works only in alphabetical sorting mode.)'
}
title={t('list.sort.caseSensitive')}
description={t('list.sort.caseSensitiveDescription')}
checked={values.caseSensitive}
onChange={(val) => updateField('caseSensitive', val)}
/>
@@ -132,19 +152,17 @@ export default function SplitText({ title }: ToolComponentProps) {
)
},
{
title: 'Sorted item properties',
title: t('list.sort.sortedItemProperties'),
component: (
<Box>
<TextFieldWithDesc
description={
'Use this symbol as a joiner between items in a sorted list.'
}
description={t('list.sort.joinSeparatorDescription')}
value={values.joinSeparator}
onOwnChange={(val) => updateField('joinSeparator', val)}
/>
<CheckboxWithDesc
title={'Remove duplicates'}
description={'Delete duplicate list items.'}
title={t('list.sort.removeDuplicates')}
description={t('list.sort.removeDuplicatesDescription')}
checked={values.removeDuplicated}
onChange={(val) => updateField('removeDuplicated', val)}
/>

View File

@@ -5,10 +5,15 @@ import { lazy } from 'react';
export const tool = defineTool('list', {
name: 'Sort',
path: 'sort',
icon: 'basil:sort-outline',
icon: 'material-symbols-light:sort',
description:
'This is a super simple browser-based application that sorts items in a list and arranges them in increasing or decreasing order. You can sort the items alphabetically, numerically, or by their length. You can also remove duplicate and empty items, as well as trim individual items that have whitespace around them. You can use any separator character to separate the input list items or alternatively use a regular expression to separate them. Additionally, you can create a new delimiter for the sorted output list.',
shortDescription: 'Quickly sort a list',
"World's simplest browser-based utility for sorting list items. Input your list and specify sorting criteria to organize items in ascending or descending order. Perfect for data organization, text processing, or creating ordered lists.",
shortDescription: 'Sort list items in specified order',
keywords: ['sort'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'list.sort.name',
description: 'list.sort.description',
shortDescription: 'list.sort.shortDescription'
}
});

View File

@@ -4,10 +4,15 @@ import { lazy } from 'react';
export const tool = defineTool('list', {
name: 'Truncate',
path: 'truncate',
icon: 'mdi:format-horizontal-align-right',
icon: 'material-symbols-light:content-cut',
description:
"World's simplest browser-based utility for truncating lists. Quickly limit the number of items in your list by specifying a maximum length. Perfect for sampling data, creating previews, or managing large lists. Supports custom separators and various truncation options.",
shortDescription: 'Limit the number of items in a list',
"World's simplest browser-based utility for truncating lists. Input your list and specify the maximum number of items to keep. Perfect for data processing, list management, or limiting content length.",
shortDescription: 'Truncate list to specified number of items',
keywords: ['truncate'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'list.truncate.name',
description: 'list.truncate.description',
shortDescription: 'list.truncate.shortDescription'
}
});

View File

@@ -5,10 +5,15 @@ import { lazy } from 'react';
export const tool = defineTool('list', {
name: 'Unwrap',
path: 'unwrap',
icon: 'mdi:unwrap',
icon: 'material-symbols-light:unfold-more',
description:
'A tool to remove characters from the beginning and end of each item in a list. Perfect for cleaning up formatted data or removing unwanted wrappers.',
shortDescription: 'Remove characters around list items.',
"World's simplest browser-based utility for unwrapping list items. Input your wrapped list and specify unwrapping criteria to flatten organized items. Perfect for data processing, text manipulation, or extracting content from structured lists.",
shortDescription: 'Unwrap list items from structured format',
keywords: ['unwrap'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'list.unwrap.name',
description: 'list.unwrap.description',
shortDescription: 'list.unwrap.shortDescription'
}
});

View File

@@ -11,6 +11,7 @@ import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import SimpleRadio from '@components/options/SimpleRadio';
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
import * as Yup from 'yup';
import { useTranslation } from 'react-i18next';
interface InitialValuesType {
splitOperatorType: SplitOperatorType;
@@ -85,6 +86,7 @@ const exampleCards: CardExampleType<InitialValuesType>[] = [
];
export default function Wrap({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -117,50 +119,50 @@ export default function Wrap({ title }: ToolComponentProps) {
updateField
}) => [
{
title: 'Split Options',
title: t('list.wrap.splitOptions'),
component: (
<Box>
<SimpleRadio
onClick={() => updateField('splitOperatorType', 'symbol')}
checked={values.splitOperatorType === 'symbol'}
title={'Split by Symbol'}
title={t('list.wrap.splitBySymbol')}
/>
<SimpleRadio
onClick={() => updateField('splitOperatorType', 'regex')}
checked={values.splitOperatorType === 'regex'}
title={'Split by Regular Expression'}
title={t('list.wrap.splitByRegex')}
/>
<TextFieldWithDesc
value={values.splitSeparator}
onOwnChange={(val) => updateField('splitSeparator', val)}
description={'Separator to split the list'}
description={t('list.wrap.splitSeparatorDescription')}
/>
<TextFieldWithDesc
value={values.joinSeparator}
onOwnChange={(val) => updateField('joinSeparator', val)}
description={'Separator to join the wrapped list'}
description={t('list.wrap.joinSeparatorDescription')}
/>
<CheckboxWithDesc
checked={values.deleteEmptyItems}
onChange={(checked) => updateField('deleteEmptyItems', checked)}
title={'Remove empty items'}
title={t('list.wrap.removeEmptyItems')}
/>
</Box>
)
},
{
title: 'Wrap Options',
title: t('list.wrap.wrapOptions'),
component: (
<Box>
<TextFieldWithDesc
value={values.left}
onOwnChange={(val) => updateField('left', val)}
description={'Text to add before each item'}
description={t('list.wrap.leftTextDescription')}
/>
<TextFieldWithDesc
value={values.right}
onOwnChange={(val) => updateField('right', val)}
description={'Text to add after each item'}
description={t('list.wrap.rightTextDescription')}
/>
</Box>
)
@@ -171,16 +173,21 @@ export default function Wrap({ title }: ToolComponentProps) {
<ToolContent
title={title}
inputComponent={
<ToolTextInput title="Input List" value={input} onChange={setInput} />
<ToolTextInput
title={t('list.wrap.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult title={t('list.wrap.resultTitle')} value={result} />
}
resultComponent={<ToolTextResult title="Wrapped List" value={result} />}
initialValues={initialValues}
getGroups={getGroups}
validationSchema={validationSchema}
toolInfo={{
title: 'List Wrapping',
description:
"This tool allows you to add text before and after each item in a list. You can specify different text for the left and right sides, and control how the list is processed. It's useful for adding quotes, brackets, or other formatting to list items, preparing data for different formats, or creating structured text."
title: t('list.wrap.toolInfo.title'),
description: t('list.wrap.toolInfo.description')
}}
exampleCards={exampleCards}
input={input}

View File

@@ -5,10 +5,15 @@ import { lazy } from 'react';
export const tool = defineTool('list', {
name: 'Wrap',
path: 'wrap',
icon: 'mdi:wrap',
icon: 'material-symbols-light:wrap-text',
description:
'A tool to wrap each item in a list with custom prefix and suffix characters. Useful for formatting lists for code, markup languages, or presentation.',
shortDescription: 'Add characters around list items.',
"World's simplest browser-based utility for wrapping list items. Input your list and specify wrapping criteria to organize items into logical groups. Perfect for categorizing data, organizing information, or creating structured lists.",
shortDescription: 'Wrap list items with specified criteria',
keywords: ['wrap'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'list.wrap.name',
description: 'list.wrap.description',
shortDescription: 'list.wrap.shortDescription'
}
});

View File

@@ -7,6 +7,7 @@ import { generateArithmeticSequence } from './service';
import * as Yup from 'yup';
import { CardExampleType } from '@components/examples/ToolExamples';
import { ToolComponentProps } from '@tools/defineTool';
import { useTranslation } from 'react-i18next';
type InitialValuesType = {
firstTerm: string;
@@ -70,6 +71,7 @@ const exampleCards: CardExampleType<InitialValuesType>[] = [
];
export default function ArithmeticSequence({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [result, setResult] = useState<string>('');
return (
@@ -77,35 +79,43 @@ export default function ArithmeticSequence({ title }: ToolComponentProps) {
title={title}
inputComponent={null}
resultComponent={
<ToolTextResult title="Generated Sequence" value={result} />
<ToolTextResult
title={t('number.arithmeticSequence.resultTitle')}
value={result}
/>
}
initialValues={initialValues}
validationSchema={validationSchema}
exampleCards={exampleCards}
toolInfo={{
title: 'What is an Arithmetic Sequence?',
description:
'An arithmetic sequence is a sequence of numbers where the difference between each consecutive term is constant. This constant difference is called the common difference. Given the first term (a₁) and the common difference (d), each term can be found by adding the common difference to the previous term.'
title: t('number.arithmeticSequence.toolInfo.title'),
description: t('number.arithmeticSequence.toolInfo.description')
}}
getGroups={({ values, updateField }) => [
{
title: 'Sequence Parameters',
title: t('number.arithmeticSequence.sequenceParameters'),
component: (
<Box>
<TextFieldWithDesc
description="First term of the sequence (a₁)"
description={t(
'number.arithmeticSequence.firstTermDescription'
)}
value={values.firstTerm}
onOwnChange={(val) => updateField('firstTerm', val)}
type="number"
/>
<TextFieldWithDesc
description="Common difference between terms (d)"
description={t(
'number.arithmeticSequence.commonDifferenceDescription'
)}
value={values.commonDifference}
onOwnChange={(val) => updateField('commonDifference', val)}
type="number"
/>
<TextFieldWithDesc
description="Number of terms to generate (n)"
description={t(
'number.arithmeticSequence.numberOfTermsDescription'
)}
value={values.numberOfTerms}
onOwnChange={(val) => updateField('numberOfTerms', val)}
type="number"
@@ -114,10 +124,10 @@ export default function ArithmeticSequence({ title }: ToolComponentProps) {
)
},
{
title: 'Output Format',
title: t('number.arithmeticSequence.outputFormat'),
component: (
<TextFieldWithDesc
description="Separator between terms"
description={t('number.arithmeticSequence.separatorDescription')}
value={values.separator}
onOwnChange={(val) => updateField('separator', val)}
/>

View File

@@ -2,20 +2,17 @@ import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('number', {
name: 'Generate Arithmetic Sequence',
name: 'Arithmetic Sequence',
path: 'arithmetic-sequence',
icon: 'ic:sharp-plus',
icon: 'material-symbols:functions',
description:
'Generate an arithmetic sequence by specifying the first term (a₁), common difference (d), and number of terms (n). The tool creates a sequence where each number differs from the previous by a constant difference.',
shortDescription:
'Generate a sequence where each term differs by a constant value.',
keywords: [
'arithmetic',
'sequence',
'progression',
'numbers',
'series',
'generate'
],
component: lazy(() => import('./index'))
'Generate arithmetic sequences with specified start value, common difference, and number of terms. Create mathematical progressions for calculations or analysis.',
shortDescription: 'Generate arithmetic sequences',
keywords: ['arithmetic', 'sequence', 'math', 'progression'],
component: lazy(() => import('./index')),
i18n: {
name: 'number.arithmeticSequence.name',
description: 'number.arithmeticSequence.description',
shortDescription: 'number.arithmeticSequence.shortDescription'
}
});

View File

@@ -5,6 +5,7 @@ import { listOfIntegers } from './service';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import ToolContent from '@components/ToolContent';
import { ToolComponentProps } from '@tools/defineTool';
import { useTranslation } from 'react-i18next';
const initialValues = {
firstValue: '1',
@@ -14,6 +15,7 @@ const initialValues = {
};
export default function GenerateNumbers({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [result, setResult] = useState<string>('');
const compute = (optionsValues: typeof initialValues) => {
@@ -34,23 +36,23 @@ export default function GenerateNumbers({ title }: ToolComponentProps) {
initialValues={initialValues}
getGroups={({ values, updateField }) => [
{
title: 'Arithmetic sequence option',
title: t('number.generate.arithmeticSequenceOption'),
component: (
<Box>
<TextFieldWithDesc
description={'Start sequence from this number.'}
description={t('number.generate.startSequenceDescription')}
value={values.firstValue}
onOwnChange={(val) => updateField('firstValue', val)}
type={'number'}
/>
<TextFieldWithDesc
description={'Increase each element by this amount'}
description={t('number.generate.stepDescription')}
value={values.step}
onOwnChange={(val) => updateField('step', val)}
type={'number'}
/>
<TextFieldWithDesc
description={'Number of elements in sequence.'}
description={t('number.generate.numberOfElementsDescription')}
value={values.numberOfNumbers}
onOwnChange={(val) => updateField('numberOfNumbers', val)}
type={'number'}
@@ -59,12 +61,10 @@ export default function GenerateNumbers({ title }: ToolComponentProps) {
)
},
{
title: 'Separator',
title: t('number.generate.separator'),
component: (
<TextFieldWithDesc
description={
'Separate elements in the arithmetic sequence by this character.'
}
description={t('number.generate.separatorDescription')}
value={values.separator}
onOwnChange={(val) => updateField('separator', val)}
/>
@@ -73,7 +73,10 @@ export default function GenerateNumbers({ title }: ToolComponentProps) {
]}
compute={compute}
resultComponent={
<ToolTextResult title={'Generated numbers'} value={result} />
<ToolTextResult
title={t('number.generate.resultTitle')}
value={result}
/>
}
/>
);

View File

@@ -3,12 +3,17 @@ import { lazy } from 'react';
// import image from '@assets/text.png';
export const tool = defineTool('number', {
name: 'Generate numbers',
name: 'Generate',
path: 'generate',
shortDescription: 'Quickly calculate a list of integers in your browser',
icon: 'lsicon:number-filled',
icon: 'material-symbols:add-circle',
description:
'Quickly calculate a list of integers in your browser. To get your list, just specify the first integer, change value and total count in the options below, and this utility will generate that many integers',
keywords: ['generate'],
component: lazy(() => import('./index'))
'Generate random numbers within specified ranges. Create sequences of numbers for testing, simulations, or random data generation.',
shortDescription: 'Generate random numbers in specified ranges',
keywords: ['generate', 'random', 'numbers'],
component: lazy(() => import('./index')),
i18n: {
name: 'number.generate.name',
description: 'number.generate.description',
shortDescription: 'number.generate.shortDescription'
}
});

View File

@@ -0,0 +1,45 @@
{
"sum": {
"title": "Sum Numbers",
"description": "Calculate the sum of a list of numbers.",
"inputTitle": "Input numbers",
"resultTitle": "Sum",
"sumOptions": "Sum Options",
"ignoreNonNumeric": "Ignore non-numeric values",
"ignoreNonNumericDescription": "Skip values that are not numbers",
"toolInfo": {
"title": "Sum numbers",
"description": "This tool allows you to calculate the sum of a list of numbers. You can input numbers separated by various delimiters and get their total sum."
}
},
"generate": {
"title": "Generate Numbers",
"description": "Generate a sequence of numbers with customizable parameters.",
"resultTitle": "Generated numbers",
"arithmeticSequenceOption": "Arithmetic sequence option",
"startSequenceDescription": "Start sequence from this number.",
"stepDescription": "Increase each element by this amount",
"numberOfElementsDescription": "Number of elements in sequence.",
"separator": "Separator",
"separatorDescription": "Separate elements in the arithmetic sequence by this character.",
"toolInfo": {
"title": "Generate numbers",
"description": "This tool allows you to generate a sequence of numbers with customizable parameters. You can specify the starting value, step size, and number of elements."
}
},
"arithmeticSequence": {
"title": "Arithmetic Sequence",
"description": "Generate arithmetic sequences with customizable parameters.",
"resultTitle": "Generated Sequence",
"sequenceParameters": "Sequence Parameters",
"firstTermDescription": "First term of the sequence (a₁)",
"commonDifferenceDescription": "Common difference between terms (d)",
"numberOfTermsDescription": "Number of terms to generate (n)",
"outputFormat": "Output Format",
"separatorDescription": "Separator between terms",
"toolInfo": {
"title": "What is an Arithmetic Sequence?",
"description": "An arithmetic sequence is a sequence of numbers where the difference between each consecutive term is constant. This constant difference is called the common difference. Given the first term (a₁) and the common difference (d), each term can be found by adding the common difference to the previous term."
}
}
}

View File

@@ -9,6 +9,7 @@ import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
import { CardExampleType } from '@components/examples/ToolExamples';
import { ToolComponentProps } from '@tools/defineTool';
import ToolContent from '@components/ToolContent';
import { useTranslation } from 'react-i18next';
const initialValues = {
extractionType: 'smart' as NumberExtractionType,
@@ -118,6 +119,7 @@ const exampleCards: CardExampleType<InitialValuesType>[] = [
];
export default function SumNumbers({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -126,16 +128,16 @@ export default function SumNumbers({ title }: ToolComponentProps) {
updateField
}) => [
{
title: 'Number extraction',
title: t('number.sum.numberExtraction'),
component: extractionTypes.map(
({ title, description, type, withTextField, textValueAccessor }) =>
withTextField ? (
<RadioWithTextField
key={type}
checked={type === values.extractionType}
title={title}
title={t(`number.sum.extractionTypes.${type}.title`)}
fieldName={'extractionType'}
description={description}
description={t(`number.sum.extractionTypes.${type}.description`)}
value={
textValueAccessor ? values[textValueAccessor].toString() : ''
}
@@ -149,18 +151,18 @@ export default function SumNumbers({ title }: ToolComponentProps) {
key={title}
onClick={() => updateField('extractionType', type)}
checked={values.extractionType === type}
description={description}
title={title}
description={t(`number.sum.extractionTypes.${type}.description`)}
title={t(`number.sum.extractionTypes.${type}.title`)}
/>
)
)
},
{
title: 'Running Sum',
title: t('number.sum.runningSum'),
component: (
<CheckboxWithDesc
title={'Print Running Sum'}
description={"Display the sum as it's calculated step by step."}
title={t('number.sum.printRunningSum')}
description={t('number.sum.printRunningSumDescription')}
checked={values.printRunningSum}
onChange={(value) => updateField('printRunningSum', value)}
/>
@@ -171,8 +173,16 @@ export default function SumNumbers({ title }: ToolComponentProps) {
<ToolContent
title={title}
input={input}
inputComponent={<ToolTextInput value={input} onChange={setInput} />}
resultComponent={<ToolTextResult title={'Total'} value={result} />}
inputComponent={
<ToolTextInput
title={t('number.sum.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult title={t('number.sum.resultTitle')} value={result} />
}
initialValues={initialValues}
getGroups={getGroups}
compute={(optionsValues, input) => {
@@ -181,9 +191,8 @@ export default function SumNumbers({ title }: ToolComponentProps) {
}}
setInput={setInput}
toolInfo={{
title: 'What Is a Number Sum Calculator?',
description:
'This is an online browser-based utility for calculating the sum of a bunch of numbers. You can enter the numbers separated by a comma, space, or any other character, including the line break. You can also simply paste a fragment of textual data that contains numerical values that you want to sum up and the utility will extract them and find their sum.'
title: t('number.sum.toolInfo.title'),
description: t('number.sum.toolInfo.description')
}}
exampleCards={exampleCards}
/>

View File

@@ -3,12 +3,17 @@ import { lazy } from 'react';
// import image from '@assets/text.png';
export const tool = defineTool('number', {
name: 'Number Sum Calculator',
name: 'Sum',
path: 'sum',
icon: 'fluent:autosum-20-regular',
icon: 'material-symbols:add',
description:
'Quickly calculate the sum of numbers in your browser. To get your sum, just enter your list of numbers in the input field, adjust the separator between the numbers in the options below, and this utility will add up all these numbers.',
shortDescription: 'Quickly sum numbers',
keywords: ['sum'],
component: lazy(() => import('./index'))
'Calculate the sum of a list of numbers. Enter numbers separated by commas or newlines to get their total sum.',
shortDescription: 'Calculate sum of numbers',
keywords: ['sum', 'add', 'calculate', 'total'],
component: lazy(() => import('./index')),
i18n: {
name: 'number.sum.name',
description: 'number.sum.description',
shortDescription: 'number.sum.shortDescription'
}
});

View File

@@ -1,24 +1,26 @@
import { Box, Typography } from '@mui/material';
import React, { useContext, useEffect, useState } from 'react';
import React, { useState, useEffect, useContext } from 'react';
import ToolContent from '@components/ToolContent';
import { ToolComponentProps } from '@tools/defineTool';
import { compressPdf } from './service';
import { InitialValuesType, CompressionLevel } from './types';
import ToolPdfInput from '@components/input/ToolPdfInput';
import { GetGroupsType } from '@components/options/ToolOptions';
import ToolFileResult from '@components/result/ToolFileResult';
import SimpleRadio from '@components/options/SimpleRadio';
import { CardExampleType } from '@components/examples/ToolExamples';
import { PDFDocument } from 'pdf-lib';
import { CompressionLevel, InitialValuesType } from './types';
import { compressPdf } from './service';
import SimpleRadio from '@components/options/SimpleRadio';
import { CustomSnackBarContext } from '../../../../contexts/CustomSnackBarContext';
import { CustomSnackBarContext } from '@contexts/CustomSnackBarContext';
import { useTranslation } from 'react-i18next';
const initialValues: InitialValuesType = {
compressionLevel: 'medium'
compressionLevel: 'low'
};
const exampleCards: CardExampleType<InitialValuesType>[] = [
{
title: 'Low Compression',
description: 'Slightly reduce file size with minimal quality loss',
description: 'Minimal quality loss with slight file size reduction',
sampleText: '',
sampleResult: '',
sampleOptions: {
@@ -49,6 +51,7 @@ export default function CompressPdf({
title,
longDescription
}: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<File | null>(null);
const [result, setResult] = useState<File | null>(null);
const [resultSize, setResultSize] = useState<string>('');
@@ -77,10 +80,7 @@ export default function CompressPdf({
} catch (error) {
console.error('Error getting PDF info:', error);
setFileInfo(null);
showSnackBar(
'Error reading PDF file. Please make sure it is a valid PDF.',
'error'
);
showSnackBar(t('pdf.compressPdf.errorReadingPdf'), 'error');
}
};
@@ -112,9 +112,9 @@ export default function CompressPdf({
} catch (error) {
console.error('Error compressing PDF:', error);
showSnackBar(
`Failed to compress PDF: ${
error instanceof Error ? error.message : String(error)
}`,
t('pdf.compressPdf.errorCompressingPdf', {
error: error instanceof Error ? error.message : String(error)
}),
'error'
);
setResult(null);
@@ -130,18 +130,18 @@ export default function CompressPdf({
}[] = [
{
value: 'low',
label: 'Low Compression',
description: 'Slightly reduce file size with minimal quality loss'
label: t('pdf.compressPdf.lowCompression'),
description: t('pdf.compressPdf.lowCompressionDescription')
},
{
value: 'medium',
label: 'Medium Compression',
description: 'Balance between file size and quality'
label: t('pdf.compressPdf.mediumCompression'),
description: t('pdf.compressPdf.mediumCompressionDescription')
},
{
value: 'high',
label: 'High Compression',
description: 'Maximum file size reduction with some quality loss'
label: t('pdf.compressPdf.highCompression'),
description: t('pdf.compressPdf.highCompressionDescription')
}
];
@@ -157,26 +157,26 @@ export default function CompressPdf({
value={input}
onChange={setInput}
accept={['application/pdf']}
title={'Input PDF'}
title={t('pdf.compressPdf.inputTitle')}
/>
}
resultComponent={
<ToolFileResult
title={'Compressed PDF'}
title={t('pdf.compressPdf.resultTitle')}
value={result}
extension={'pdf'}
loading={isProcessing}
loadingText={'Compressing PDF'}
loadingText={t('pdf.compressPdf.compressingPdf')}
/>
}
getGroups={({ values, updateField }) => [
{
title: 'Compression Settings',
title: t('pdf.compressPdf.compressionSettings'),
component: (
<Box>
<Box>
<Typography variant="subtitle2" sx={{ mb: 1 }}>
Compression Level
{t('pdf.compressPdf.compressionLevel')}
</Typography>
{compressionOptions.map((option) => (
@@ -201,14 +201,17 @@ export default function CompressPdf({
}}
>
<Typography variant="body2">
File size: <strong>{fileInfo.size}</strong>
{t('pdf.compressPdf.fileSize')}:{' '}
<strong>{fileInfo.size}</strong>
</Typography>
<Typography variant="body2">
Pages: <strong>{fileInfo.pages}</strong>
{t('pdf.compressPdf.pages')}:{' '}
<strong>{fileInfo.pages}</strong>
</Typography>
{resultSize && (
<Typography variant="body2">
Compressed file size: <strong>{resultSize}</strong>
{t('pdf.compressPdf.compressedFileSize')}:{' '}
<strong>{resultSize}</strong>
</Typography>
)}
</Box>

View File

@@ -24,5 +24,10 @@ export const tool = defineTool('pdf', {
],
longDescription:
'Compress PDF files securely in your browser using Ghostscript. Your files never leave your device, ensuring complete privacy while reducing file sizes for email sharing, uploading to websites, or saving storage space. Powered by WebAssembly technology.',
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'pdf.compressPdf.name',
description: 'pdf.compressPdf.description',
shortDescription: 'pdf.compressPdf.shortDescription'
}
});

View File

@@ -0,0 +1,72 @@
{
"mergePdf": {
"title": "Merge PDF",
"description": "Combine multiple PDF files into a single document.",
"inputTitle": "Input PDFs",
"resultTitle": "Merged PDF",
"mergingPdfs": "Merging PDFs",
"pdfOptions": "PDF Options",
"sortByFileName": "Sort by file name",
"sortByFileNameDescription": "Sort PDFs alphabetically by file name",
"sortByUploadOrder": "Sort by upload order",
"sortByUploadOrderDescription": "Keep PDFs in the order they were uploaded",
"toolInfo": {
"title": "Merge PDF Files",
"description": "This tool allows you to combine multiple PDF files into a single document. You can choose how to sort the PDFs and the tool will merge them in the specified order."
}
},
"compressPdf": {
"title": "Compress PDF",
"description": "Reduce PDF file size while maintaining quality.",
"inputTitle": "Input PDF",
"resultTitle": "Compressed PDF",
"compressingPdf": "Compressing PDF",
"compressionOptions": "Compression Options",
"qualityDescription": "Compression quality (1-100)",
"qualityPlaceholder": "Quality",
"toolInfo": {
"title": "Compress PDF",
"description": "This tool allows you to compress PDF files to reduce their size while maintaining acceptable quality. You can adjust the compression level to balance between file size and quality."
}
},
"splitPdf": {
"title": "Split PDF",
"description": "Extract specific pages from a PDF document.",
"inputTitle": "Input PDF",
"resultTitle": "Extracted PDF",
"extractingPages": "Extracting pages",
"pageSelection": "Page Selection",
"pdfPageCount": "PDF has {{count}} page{{count !== 1 ? 's' : ''}}",
"pageRangesDescription": "Enter page numbers or ranges separated by commas (e.g., 1,3,5-7)",
"pageRangesPlaceholder": "e.g., 1,5-8",
"pageExtractionPreview": "{{count}} page{{count !== 1 ? 's' : ''}} will be extracted",
"toolInfo": {
"title": "Split PDF",
"description": "This tool allows you to extract specific pages from a PDF document. You can specify individual pages or ranges of pages to extract."
}
},
"rotatePdf": {
"title": "Rotate PDF",
"description": "Rotate pages in a PDF document.",
"inputTitle": "Input PDF",
"resultTitle": "Rotated PDF",
"rotatingPages": "Rotating pages",
"rotationSettings": "Rotation Settings",
"rotationAngle": "Rotation Angle",
"angleOptions": {
"clockwise90": "90° Clockwise",
"upsideDown180": "180° (Upside down)",
"counterClockwise270": "270° (90° Counter-clockwise)"
},
"applyToAllPages": "Apply to all pages",
"pdfPageCount": "PDF has {{count}} page{{count !== 1 ? 's' : ''}}",
"pageRangesDescription": "Enter page numbers or ranges separated by commas (e.g., 1,3,5-7)",
"pageRangesPlaceholder": "e.g., 1,5-8",
"allPagesWillBeRotated": "All {{count}} pages will be rotated",
"pagesWillBeRotated": "{{count}} page{{count !== 1 ? 's' : ''}} will be rotated",
"toolInfo": {
"title": "How to Use the Rotate PDF Tool",
"description": "This tool allows you to rotate pages in a PDF document. You can rotate all pages or specify individual pages to rotate. Choose a rotation angle: 90° Clockwise, 180° (Upside down), or 270° (90° Counter-clockwise). To rotate specific pages, uncheck \"Apply to all pages\" and enter page numbers or ranges separated by commas (e.g., 1,3,5-7)."
}
}
}

View File

@@ -6,8 +6,10 @@ import { mergePdf } from './service';
import ToolMultiPdfInput, {
MultiPdfInput
} from '@components/input/ToolMultiplePdfInput';
import { useTranslation } from 'react-i18next';
export default function MergePdf({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<MultiPdfInput[]>([]);
const [result, setResult] = useState<File | null>(null);
const [isProcessing, setIsProcessing] = useState<boolean>(false);
@@ -42,24 +44,23 @@ export default function MergePdf({ title }: ToolComponentProps) {
setInput(pdfInputs);
}}
accept={['application/pdf']}
title={'Input PDF'}
title={t('pdf.merge.inputTitle')}
type="pdf"
/>
}
getGroups={null}
resultComponent={
<ToolFileResult
title={'Output merged PDF'}
title={t('pdf.merge.resultTitle')}
value={result}
extension={'pdf'}
loading={isProcessing}
loadingText={'Extracting pages'}
loadingText={t('pdf.merge.loadingText')}
/>
}
toolInfo={{
title: 'How to Use the Merge PDF Tool?',
description: `This tool allows you to merge multiple PDF files into a single document.
To use the tool, simply upload the PDF files you want to merge. The tool will then combine all pages from the input files into a single PDF document.`
title: t('pdf.merge.toolInfo.title'),
description: t('pdf.merge.toolInfo.description')
}}
/>
);

View File

@@ -8,5 +8,10 @@ export const meta = defineTool('pdf', {
icon: 'material-symbols-light:merge',
component: lazy(() => import('./index')),
keywords: ['pdf', 'merge', 'extract', 'pages', 'combine', 'document'],
path: 'merge-pdf'
path: 'merge-pdf',
i18n: {
name: 'pdf.mergePdf.name',
description: 'pdf.mergePdf.description',
shortDescription: 'pdf.mergePdf.shortDescription'
}
});

View File

@@ -9,5 +9,10 @@ export const meta = defineTool('pdf', {
icon: 'material-symbols:import-contacts',
component: lazy(() => import('./index')),
keywords: ['pdf', 'epub', 'convert', 'ebook'],
path: 'pdf-to-epub'
path: 'pdf-to-epub',
i18n: {
name: 'pdf.pdfToEpub.name',
description: 'pdf.pdfToEpub.description',
shortDescription: 'pdf.pdfToEpub.shortDescription'
}
});

View File

@@ -23,5 +23,10 @@ export const tool = defineTool('pdf', {
],
longDescription:
'Add password protection to your PDF files securely in your browser. Your files never leave your device, ensuring complete privacy while securing your documents with password encryption. Perfect for protecting sensitive information, confidential documents, or personal data.',
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'pdf.protectPdf.name',
description: 'pdf.protectPdf.description',
shortDescription: 'pdf.protectPdf.shortDescription'
}
});

View File

@@ -1,16 +1,16 @@
import { Box, FormControlLabel, Switch, Typography } from '@mui/material';
import React, { useEffect, useState } from 'react';
import { Box, Typography, FormControlLabel, Switch } from '@mui/material';
import { useEffect, useState } from 'react';
import ToolFileResult from '@components/result/ToolFileResult';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import ToolContent from '@components/ToolContent';
import { ToolComponentProps } from '@tools/defineTool';
import ToolPdfInput from '@components/input/ToolPdfInput';
import ToolFileResult from '@components/result/ToolFileResult';
import { parsePageRanges, rotatePdf } from './service';
import { CardExampleType } from '@components/examples/ToolExamples';
import { PDFDocument } from 'pdf-lib';
import { InitialValuesType, RotationAngle } from './types';
import { parsePageRanges, rotatePdf } from './service';
import ToolPdfInput from '@components/input/ToolPdfInput';
import SimpleRadio from '@components/options/SimpleRadio';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import { isArray } from 'lodash';
import { InitialValuesType, RotationAngle } from './types';
import { useTranslation } from 'react-i18next';
const initialValues: InitialValuesType = {
rotationAngle: 90,
@@ -21,7 +21,7 @@ const initialValues: InitialValuesType = {
const exampleCards: CardExampleType<InitialValuesType>[] = [
{
title: 'Rotate All Pages 90°',
description: 'Rotate all pages in the document 90 degrees clockwise',
description: 'Rotate all pages in the PDF by 90 degrees clockwise',
sampleText: '',
sampleResult: '',
sampleOptions: {
@@ -32,7 +32,7 @@ const exampleCards: CardExampleType<InitialValuesType>[] = [
},
{
title: 'Rotate Specific Pages 180°',
description: 'Rotate only pages 1 and 3 by 180 degrees',
description: 'Rotate pages 1 and 3 by 180 degrees',
sampleText: '',
sampleResult: '',
sampleOptions: {
@@ -58,6 +58,7 @@ export default function RotatePdf({
title,
longDescription
}: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<File | null>(null);
const [result, setResult] = useState<File | null>(null);
const [isProcessing, setIsProcessing] = useState<boolean>(false);
@@ -90,7 +91,9 @@ export default function RotatePdf({
if (applyToAllPages) {
setPageRangePreview(
totalPages > 0 ? `All ${totalPages} pages will be rotated` : ''
totalPages > 0
? t('pdf.rotatePdf.allPagesWillBeRotated', { count: totalPages })
: ''
);
return;
}
@@ -102,9 +105,7 @@ export default function RotatePdf({
try {
const count = parsePageRanges(pageRanges, totalPages).length;
setPageRangePreview(
`${count} page${count !== 1 ? 's' : ''} will be rotated`
);
setPageRangePreview(t('pdf.rotatePdf.pagesWillBeRotated', { count }));
} catch (error) {
setPageRangePreview('');
}
@@ -124,9 +125,9 @@ export default function RotatePdf({
}
};
const angleOptions: { value: RotationAngle; label: string }[] = [
{ value: 90, label: '90° Clockwise' },
{ value: 180, label: '180° (Upside down)' },
{ value: 270, label: '270° (90° Counter-clockwise)' }
{ value: 90, label: t('pdf.rotatePdf.angleOptions.clockwise90') },
{ value: 180, label: t('pdf.rotatePdf.angleOptions.upsideDown180') },
{ value: 270, label: t('pdf.rotatePdf.angleOptions.counterClockwise270') }
];
return (
<ToolContent
@@ -141,25 +142,25 @@ export default function RotatePdf({
value={input}
onChange={setInput}
accept={['application/pdf']}
title={'Input PDF'}
title={t('pdf.rotatePdf.inputTitle')}
/>
}
resultComponent={
<ToolFileResult
title={'Rotated PDF'}
title={t('pdf.rotatePdf.resultTitle')}
value={result}
extension={'pdf'}
loading={isProcessing}
loadingText={'Rotating pages'}
loadingText={t('pdf.rotatePdf.rotatingPages')}
/>
}
getGroups={({ values, updateField }) => [
{
title: 'Rotation Settings',
title: t('pdf.rotatePdf.rotationSettings'),
component: (
<Box>
<Typography variant="subtitle2" sx={{ mb: 1 }}>
Rotation Angle
{t('pdf.rotatePdf.rotationAngle')}
</Typography>
{angleOptions.map((angleOption) => (
<SimpleRadio
@@ -182,7 +183,7 @@ export default function RotatePdf({
}}
/>
}
label="Apply to all pages"
label={t('pdf.rotatePdf.applyToAllPages')}
/>
</Box>
@@ -190,7 +191,7 @@ export default function RotatePdf({
<Box sx={{ mt: 2 }}>
{totalPages > 0 && (
<Typography variant="body2" sx={{ mb: 1 }}>
PDF has {totalPages} page{totalPages !== 1 ? 's' : ''}
{t('pdf.rotatePdf.pdfPageCount', { count: totalPages })}
</Typography>
)}
<TextFieldWithDesc
@@ -198,10 +199,8 @@ export default function RotatePdf({
onOwnChange={(val) => {
updateField('pageRanges', val);
}}
description={
'Enter page numbers or ranges separated by commas (e.g., 1,3,5-7)'
}
placeholder={'e.g., 1,5-8'}
description={t('pdf.rotatePdf.pageRangesDescription')}
placeholder={t('pdf.rotatePdf.pageRangesPlaceholder')}
/>
{pageRangePreview && (
<Typography
@@ -219,24 +218,8 @@ export default function RotatePdf({
]}
onValuesChange={onValuesChange}
toolInfo={{
title: 'How to Use the Rotate PDF Tool',
description: `This tool allows you to rotate pages in a PDF document. You can rotate all pages or specify individual pages to rotate.
Choose a rotation angle:
- 90° Clockwise
- 180° (Upside down)
- 270° (90° Counter-clockwise)
To rotate specific pages:
1. Uncheck "Apply to all pages"
2. Enter page numbers or ranges separated by commas (e.g., 1,3,5-7)
Examples:
- "1,5,9" rotates pages 1, 5, and 9
- "1-5" rotates pages 1 through 5
- "1,3-5,8-10" rotates pages 1, 3, 4, 5, 8, 9, and 10
${longDescription}`
title: t('pdf.rotatePdf.toolInfo.title'),
description: t('pdf.rotatePdf.toolInfo.description')
}}
/>
);

View File

@@ -8,6 +8,7 @@ import { parsePageRanges, splitPdf } from './service';
import { CardExampleType } from '@components/examples/ToolExamples';
import { PDFDocument } from 'pdf-lib';
import ToolPdfInput from '@components/input/ToolPdfInput';
import { useTranslation } from 'react-i18next';
type InitialValuesType = {
pageRanges: string;
@@ -48,6 +49,7 @@ const exampleCards: CardExampleType<InitialValuesType>[] = [
];
export default function SplitPdf({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<File | null>(null);
const [result, setResult] = useState<File | null>(null);
const [isProcessing, setIsProcessing] = useState<boolean>(false);
@@ -83,9 +85,7 @@ export default function SplitPdf({ title }: ToolComponentProps) {
}
try {
const count = parsePageRanges(pageRanges, totalPages).length;
setPageRangePreview(
`${count} page${count !== 1 ? 's' : ''} will be extracted`
);
setPageRangePreview(t('pdf.splitPdf.pageExtractionPreview', { count }));
} catch (error) {
setPageRangePreview('');
}
@@ -118,26 +118,26 @@ export default function SplitPdf({ title }: ToolComponentProps) {
value={input}
onChange={setInput}
accept={['application/pdf']}
title={'Input PDF'}
title={t('pdf.splitPdf.inputTitle')}
/>
}
resultComponent={
<ToolFileResult
title={'Output PDF with selected pages'}
title={t('pdf.splitPdf.resultTitle')}
value={result}
extension={'pdf'}
loading={isProcessing}
loadingText={'Extracting pages'}
loadingText={t('pdf.splitPdf.extractingPages')}
/>
}
getGroups={({ values, updateField }) => [
{
title: 'Page Selection',
title: t('pdf.splitPdf.pageSelection'),
component: (
<Box>
{totalPages > 0 && (
<Typography variant="body2" sx={{ mb: 1 }}>
PDF has {totalPages} page{totalPages !== 1 ? 's' : ''}
{t('pdf.splitPdf.pdfPageCount', { count: totalPages })}
</Typography>
)}
<TextFieldWithDesc
@@ -145,10 +145,8 @@ export default function SplitPdf({ title }: ToolComponentProps) {
onOwnChange={(val) => {
updateField('pageRanges', val);
}}
description={
'Enter page numbers or ranges separated by commas (e.g., 1,3,5-7)'
}
placeholder={'e.g., 1,5-8'}
description={t('pdf.splitPdf.pageRangesDescription')}
placeholder={t('pdf.splitPdf.pageRangesPlaceholder')}
/>
{pageRangePreview && (
<Typography
@@ -164,15 +162,8 @@ export default function SplitPdf({ title }: ToolComponentProps) {
]}
onValuesChange={onValuesChange}
toolInfo={{
title: 'How to Use the Split PDF Tool',
description: `This tool allows you to extract specific pages from a PDF document. You can specify individual page numbers (e.g., 1,3,5) or page ranges (e.g., 2-6) or a combination of both (e.g., 1,3-5,8).
Leave the page ranges field empty to include all pages from the PDF.
Examples:
- "1,5,9" extracts pages 1, 5, and 9
- "1-5" extracts pages 1 through 5
- "1,3-5,8-10" extracts pages 1, 3, 4, 5, 8, 9, and 10`
title: t('pdf.splitPdf.toolInfo.title'),
description: t('pdf.splitPdf.toolInfo.description')
}}
/>
);

View File

@@ -9,5 +9,10 @@ export const meta = defineTool('pdf', {
icon: 'material-symbols-light:call-split-rounded',
component: lazy(() => import('./index')),
keywords: ['pdf', 'split', 'extract', 'pages', 'range', 'document'],
path: 'split-pdf'
path: 'split-pdf',
i18n: {
name: 'pdf.splitPdf.name',
description: 'pdf.splitPdf.description',
shortDescription: 'pdf.splitPdf.shortDescription'
}
});

View File

@@ -9,6 +9,7 @@ import { GetGroupsType } from '@components/options/ToolOptions';
import { Box } from '@mui/material';
import SimpleRadio from '@components/options/SimpleRadio';
import { InitialValuesType } from './types';
import { useTranslation } from 'react-i18next';
const initialValues: InitialValuesType = {
mode: 'encode'
@@ -33,6 +34,7 @@ const exampleCards: CardExampleType<InitialValuesType>[] = [
];
export default function Base64({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -45,18 +47,18 @@ export default function Base64({ title }: ToolComponentProps) {
updateField
}) => [
{
title: 'Base64 Options',
title: t('string.base64.optionsTitle'),
component: (
<Box>
<SimpleRadio
onClick={() => updateField('mode', 'encode')}
checked={values.mode === 'encode'}
title={'Base64 Encode'}
title={t('string.base64.encode')}
/>
<SimpleRadio
onClick={() => updateField('mode', 'decode')}
checked={values.mode === 'decode'}
title={'Base64 Decode'}
title={t('string.base64.decode')}
/>
</Box>
)
@@ -67,15 +69,20 @@ export default function Base64({ title }: ToolComponentProps) {
<ToolContent
title={title}
inputComponent={
<ToolTextInput title="Input Data" value={input} onChange={setInput} />
<ToolTextInput
title={t('string.base64.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult title={t('string.base64.resultTitle')} value={result} />
}
resultComponent={<ToolTextResult title="Result" value={result} />}
initialValues={initialValues}
getGroups={getGroups}
toolInfo={{
title: 'What is Base64?',
description:
'Base64 is an encoding scheme that represents data in an ASCII string format by translating it into a radix-64 representation. Although it can be used to encode strings, it is commonly used to encode binary data for transmission over media that are designed to deal with textual data.'
title: t('string.base64.toolInfo.title'),
description: t('string.base64.toolInfo.description')
}}
exampleCards={exampleCards}
input={input}

View File

@@ -9,5 +9,10 @@ export const tool = defineTool('string', {
'A simple tool to encode or decode data using Base64, which is commonly used in web applications.',
shortDescription: 'Encode or decode data using Base64.',
keywords: ['base64'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'string.base64.name',
description: 'string.base64.description',
shortDescription: 'string.base64.shortDescription'
}
});

View File

@@ -12,5 +12,11 @@ export const tool = defineTool('string', {
longDescription:
'With this online tool, you can censor certain words in any text. You can specify a list of unwanted words (such as swear words or secret words) and the program will replace them with alternative words and create a safe-to-read text. The words can be specified in a multi-line text field in the options by entering one word per line.',
keywords: ['text', 'censor', 'words', 'characters'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'string.censor.name',
description: 'string.censor.description',
shortDescription: 'string.censor.shortDescription',
longDescription: 'string.censor.longDescription'
}
});

View File

@@ -12,5 +12,10 @@ export const tool = defineTool('string', {
longDescription:
'This tool creates a palindrome from the given string. It does it by generating a copy of the string, reversing it, and appending it at the end of the original string. This method creates a palindrome with the last character duplicated twice. There is also another way to do it, which deletes the first letter of the reversed copy. In this case, when the string and the copy are joined together, you also get a palindrome but without the repeating last character. You can compare the two types of palindromes by switching between them in the options. You can also enable the multi-line mode that will create palindromes of every string on every line. Stringabulous!',
keywords: ['create', 'palindrome'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'string.createPalindrome.name',
description: 'string.createPalindrome.description',
shortDescription: 'string.createPalindrome.shortDescription'
}
});

View File

@@ -7,8 +7,13 @@ export const tool = defineTool('string', {
path: 'extract-substring',
icon: 'material-symbols-light:content-cut',
description:
"World's simplest browser-based utility for extracting substrings from text. Easily extract specific portions of text by specifying start position and length. Perfect for parsing data, isolating specific parts of text, or data extraction tasks. Supports multi-line text processing and character-level precision.",
shortDescription: 'Extract specific portions of text by position and length',
"World's simplest browser-based utility for extracting substrings from text. Input your text and specify start and end positions to extract the desired portion. Perfect for data processing, text analysis, or extracting specific content from larger text blocks.",
shortDescription: 'Extract a portion of text between specified positions',
keywords: ['extract', 'substring'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'string.extractSubstring.name',
description: 'string.extractSubstring.description',
shortDescription: 'string.extractSubstring.shortDescription'
}
});

View File

@@ -0,0 +1,196 @@
{
"uppercase": {
"title": "Convert to Uppercase",
"description": "Convert text to uppercase letters.",
"inputTitle": "Input text",
"resultTitle": "Uppercase text"
},
"reverse": {
"title": "Reverse Text",
"description": "Reverse the order of characters in text.",
"inputTitle": "Input text",
"resultTitle": "Reversed text",
"processMultiLine": "Process as multi-line text (reverse each line separately)",
"skipEmptyLines": "Skip empty lines",
"trimWhitespace": "Trim whitespace from lines"
},
"base64": {
"title": "Base64 Encoder/Decoder",
"description": "Encode or decode text using Base64 encoding.",
"inputTitle": "Input Data",
"resultTitle": "Result",
"optionsTitle": "Base64 Options",
"encode": "Base64 Encode",
"decode": "Base64 Decode",
"toolInfo": {
"title": "What is Base64?",
"description": "Base64 is an encoding scheme that represents data in an ASCII string format by translating it into a radix-64 representation. Although it can be used to encode strings, it is commonly used to encode binary data for transmission over media that are designed to deal with textual data."
}
},
"truncate": {
"title": "Truncate Text",
"description": "Shorten text to a specified length.",
"inputTitle": "Input text",
"resultTitle": "Truncated text",
"truncationSide": "Truncation Side",
"rightSideTruncation": "Right-side Truncation",
"rightSideDescription": "Remove characters from the end of the text.",
"leftSideTruncation": "Left-side Truncation",
"leftSideDescription": "Remove characters from the start of the text.",
"lengthAndLines": "Length and Lines",
"maxLengthDescription": "Number of characters to leave in the text.",
"numberPlaceholder": "Number",
"lineByLineTruncating": "Line-by-line Truncating",
"lineByLineDescription": "Truncate each line separately.",
"suffixAndAffix": "Suffix and Affix",
"addTruncationIndicator": "Add Truncation Indicator",
"indicatorDescription": "Characters to add at the end (or start) of the text. Note: They count towards the length.",
"charactersPlaceholder": "Characters",
"toolInfo": {
"title": "Truncate text",
"description": "Load your text in the input form on the left and you will automatically get truncated text on the right."
}
},
"quote": {
"title": "Text Quoter",
"description": "Add quotes around text with customizable options.",
"inputTitle": "Input Text",
"resultTitle": "Quoted Text",
"quoteOptions": "Quote Options",
"leftQuoteDescription": "Left quote character(s)",
"rightQuoteDescription": "Right quote character(s)",
"allowDoubleQuotation": "Allow double quotation",
"quoteEmptyLines": "Quote empty lines",
"processAsMultiLine": "Process as multi-line text",
"toolInfo": {
"title": "Text Quoter",
"description": "This tool allows you to add quotes around text. You can choose different quote characters, handle multi-line text, and control how empty lines are processed. It's useful for preparing text for programming, formatting data, or creating stylized text."
}
},
"split": {
"title": "Split Text",
"description": "Split text into parts based on various criteria.",
"resultTitle": "Text pieces",
"splitSeparatorOptions": "Split separator options",
"symbolTitle": "Use a Symbol for Splitting",
"symbolDescription": "Character that will be used to break text into parts. (Space by default.)",
"regexTitle": "Use a Regex for Splitting",
"regexDescription": "Regular expression that will be used to break text into parts. (Multiple spaces by default.)",
"lengthTitle": "Use Length for Splitting",
"lengthDescription": "Number of symbols that will be put in each output chunk.",
"chunksTitle": "Use a Number of Chunks",
"chunksDescription": "Number of chunks of equal length in the output.",
"outputSeparatorOptions": "Output separator options",
"outputSeparatorDescription": "Character that will be put between the split chunks. (It's newline \"\\n\" by default.)",
"charBeforeChunkDescription": "Character before each chunk",
"charAfterChunkDescription": "Character after each chunk"
},
"join": {
"title": "Join Text",
"description": "Join text pieces together with customizable separators.",
"inputTitle": "Text Pieces",
"resultTitle": "Joined Text",
"textMergedOptions": "Text Merged Options",
"joinCharacterPlaceholder": "Join Character",
"joinCharacterDescription": "Symbol that connects broken pieces of text. (Space by default.)",
"blankLinesAndTrailingSpaces": "Blank Lines and Trailing Spaces",
"deleteBlankTitle": "Delete Blank Lines",
"deleteBlankDescription": "Delete lines that don't have text symbols.",
"deleteTrailingTitle": "Delete Trailing Spaces",
"deleteTrailingDescription": "Remove spaces and tabs at the end of the lines.",
"toolInfo": {
"title": "What Is a Text Joiner?",
"description": "With this tool you can join parts of the text together. It takes a list of text values, separated by newlines, and merges them together. You can set the character that will be placed between the parts of the combined text. Also, you can ignore all empty lines and remove spaces and tabs at the end of all lines. Textabulous!"
}
},
"rotate": {
"title": "Rotate Text",
"description": "Rotate characters in text by specified positions.",
"inputTitle": "Input Text",
"resultTitle": "Rotated Text",
"rotationOptions": "Rotation Options",
"stepDescription": "Number of positions to rotate",
"rotateRight": "Rotate Right",
"rotateLeft": "Rotate Left",
"processAsMultiLine": "Process as multi-line text (rotate each line separately)",
"toolInfo": {
"title": "String Rotation",
"description": "This tool allows you to rotate characters in a string by a specified number of positions. You can rotate to the left or right, and process multi-line text by rotating each line separately. String rotation is useful for simple text transformations, creating patterns, or implementing basic encryption techniques."
}
},
"repeat": {
"title": "Repeat Text",
"description": "Repeat text multiple times with customizable separators.",
"inputTitle": "Input text",
"resultTitle": "Repeated text",
"textRepetitions": "Text Repetitions",
"repeatAmountDescription": "Number of repetitions.",
"numberPlaceholder": "Number",
"repetitionsDelimiter": "Repetitions Delimiter",
"delimiterDescription": "Delimiter for output copies.",
"delimiterPlaceholder": "Delimiter",
"toolInfo": {
"title": "Repeat text",
"description": "This tool allows you to repeat a given text multiple times with an optional separator."
}
},
"rot13": {
"title": "ROT13 Encoder/Decoder",
"description": "Encode or decode text using ROT13 cipher.",
"inputTitle": "Input Text",
"resultTitle": "ROT13 Result",
"toolInfo": {
"title": "What Is ROT13?",
"description": "ROT13 (rotate by 13 places) is a simple letter substitution cipher that replaces a letter with the 13th letter after it in the alphabet. ROT13 is a special case of the Caesar cipher which was developed in ancient Rome. Because there are 26 letters in the English alphabet, ROT13 is its own inverse; that is, to undo ROT13, the same algorithm is applied, so the same action can be used for encoding and decoding."
}
},
"toMorse": {
"title": "To Morse",
"description": "Convert text to Morse code.",
"resultTitle": "Morse code",
"shortSignal": "Short Signal",
"dotSymbolDescription": "Symbol that will correspond to the dot in Morse code.",
"longSignal": "Long Signal",
"dashSymbolDescription": "Symbol that will correspond to the dash in Morse code."
},
"statistic": {
"title": "Text Statistics",
"description": "Analyze text and generate comprehensive statistics.",
"inputTitle": "Input text",
"resultTitle": "Text Statistics",
"delimitersOptions": "Delimiters Options",
"sentenceDelimitersPlaceholder": "e.g. ., !, ?, ...",
"sentenceDelimitersDescription": "Enter custom characters used to delimit sentences in your language (separated by comma) or leave it blank for default.",
"wordDelimitersPlaceholder": "eg. \\s.,;:!?\"«»()…",
"wordDelimitersDescription": "Enter custom Regex to count Words or leave it blank for default.",
"statisticsOptions": "Statistics Options",
"wordFrequencyAnalysis": "Word Frequency Analysis",
"wordFrequencyAnalysisDescription": "Count how often each word appears in the text",
"characterFrequencyAnalysis": "Character Frequency Analysis",
"characterFrequencyAnalysisDescription": "Count how often each character appears in the text",
"includeEmptyLines": "Include Empty Lines",
"includeEmptyLinesDescription": "Include blank lines when counting lines",
"toolInfo": {
"title": "What is a {{title}}?",
"description": "This tool allows you to analyze text and generate comprehensive statistics including character count, word count, line count, and frequency analysis of characters and words."
}
},
"textReplacer": {
"title": "Text Replacer",
"description": "Replace text patterns with new content.",
"inputTitle": "Text to replace",
"resultTitle": "Text with replacements",
"searchText": "Search text",
"findPatternInText": "Find This Pattern in Text",
"searchPatternDescription": "Enter the text pattern that you want to replace.",
"findPatternUsingRegexp": "Find a Pattern Using a RegExp",
"regexpDescription": "Enter the regular expression that you want to replace.",
"replaceText": "Replace Text",
"replacePatternDescription": "Enter the pattern to use for replacement.",
"newTextPlaceholder": "New text",
"toolInfo": {
"title": "Text Replacer",
"description": "Easily replace specific text in your content with this simple, browser-based tool. Just input your text, set the text you want to replace and the replacement value, and instantly get the updated version."
}
}
}

View File

@@ -9,6 +9,7 @@ import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
import { CardExampleType } from '@components/examples/ToolExamples';
import { ToolComponentProps } from '@tools/defineTool';
import { useTranslation } from 'react-i18next';
const initialValues = {
joinCharacter: '',
@@ -107,6 +108,7 @@ s
];
export default function JoinText({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
const compute = (optionsValues: InitialValuesType, input: any) => {
@@ -119,25 +121,25 @@ export default function JoinText({ title }: ToolComponentProps) {
updateField
}) => [
{
title: 'Text Merged Options',
title: t('string.join.textMergedOptions'),
component: (
<TextFieldWithDesc
placeholder={mergeOptions.placeholder}
placeholder={t('string.join.joinCharacterPlaceholder')}
value={values['joinCharacter']}
onOwnChange={(value) => updateField(mergeOptions.accessor, value)}
description={mergeOptions.description}
description={t('string.join.joinCharacterDescription')}
/>
)
},
{
title: 'Blank Lines and Trailing Spaces',
title: t('string.join.blankLinesAndTrailingSpaces'),
component: blankTrailingOptions.map((option) => (
<CheckboxWithDesc
key={option.accessor}
title={option.title}
title={t(`string.join.${option.accessor}Title`)}
checked={!!values[option.accessor]}
onChange={(value) => updateField(option.accessor, value)}
description={option.description}
description={t(`string.join.${option.accessor}Description`)}
/>
))
}
@@ -151,17 +153,18 @@ export default function JoinText({ title }: ToolComponentProps) {
setInput={setInput}
inputComponent={
<ToolTextInput
title={'Text Pieces'}
title={t('string.join.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={<ToolTextResult title={'Joined Text'} value={result} />}
resultComponent={
<ToolTextResult title={t('string.join.resultTitle')} value={result} />
}
getGroups={getGroups}
toolInfo={{
title: 'What Is a Text Joiner?',
description:
'With this tool you can join parts of the text together. It takes a list of text values, separated by newlines, and merges them together. You can set the character that will be placed between the parts of the combined text. Also, you can ignore all empty lines and remove spaces and tabs at the end of all lines. Textabulous!'
title: t('string.join.toolInfo.title'),
description: t('string.join.toolInfo.description')
}}
exampleCards={exampleCards}
/>

View File

@@ -2,12 +2,17 @@ import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('string', {
name: 'Join',
path: 'join',
name: 'Text Joiner',
icon: 'tabler:arrows-join',
icon: 'material-symbols-light:join',
description:
"World's Simplest Text Tool World's simplest browser-based utility for joining text. Load your text in the input form on the left and you'll automatically get merged text on the right. Powerful, free, and fast. Load text get joined lines",
shortDescription: 'Quickly merge texts',
keywords: ['text', 'join'],
component: lazy(() => import('./index'))
"World's simplest browser-based utility for joining text elements. Input your text elements and specify a separator to combine them into a single string. Perfect for data processing, text manipulation, or creating formatted output from lists.",
shortDescription: 'Join text elements with a specified separator',
keywords: ['join'],
component: lazy(() => import('./index')),
i18n: {
name: 'string.join.name',
description: 'string.join.description',
shortDescription: 'string.join.shortDescription'
}
});

View File

@@ -10,5 +10,10 @@ export const tool = defineTool('string', {
"World's simplest browser-based utility for checking if text is a palindrome. Instantly verify if your text reads the same forward and backward. Perfect for word puzzles, linguistic analysis, or validating symmetrical text patterns. Supports various delimiters and multi-word palindrome detection.",
shortDescription: 'Check if text reads the same forward and backward',
keywords: ['palindrome'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'string.palindrome.name',
description: 'string.palindrome.description',
shortDescription: 'string.palindrome.shortDescription'
}
});

View File

@@ -9,6 +9,7 @@ import { ToolComponentProps } from '@tools/defineTool';
import { GetGroupsType } from '@components/options/ToolOptions';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
import { useTranslation } from 'react-i18next';
interface InitialValuesType {
leftQuote: string;
@@ -70,6 +71,7 @@ const exampleCards: CardExampleType<InitialValuesType>[] = [
];
export default function Quote({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -93,33 +95,33 @@ export default function Quote({ title }: ToolComponentProps) {
updateField
}) => [
{
title: 'Quote Options',
title: t('string.quote.quoteOptions'),
component: (
<Box>
<TextFieldWithDesc
value={values.leftQuote}
onOwnChange={(val) => updateField('leftQuote', val)}
description={'Left quote character(s)'}
description={t('string.quote.leftQuoteDescription')}
/>
<TextFieldWithDesc
value={values.rightQuote}
onOwnChange={(val) => updateField('rightQuote', val)}
description={'Right quote character(s)'}
description={t('string.quote.rightQuoteDescription')}
/>
<CheckboxWithDesc
checked={values.doubleQuotation}
onChange={(checked) => updateField('doubleQuotation', checked)}
title={'Allow double quotation'}
title={t('string.quote.allowDoubleQuotation')}
/>
<CheckboxWithDesc
checked={values.emptyQuoting}
onChange={(checked) => updateField('emptyQuoting', checked)}
title={'Quote empty lines'}
title={t('string.quote.quoteEmptyLines')}
/>
<CheckboxWithDesc
checked={values.multiLine}
onChange={(checked) => updateField('multiLine', checked)}
title={'Process as multi-line text'}
title={t('string.quote.processAsMultiLine')}
/>
</Box>
)
@@ -130,15 +132,20 @@ export default function Quote({ title }: ToolComponentProps) {
<ToolContent
title={title}
inputComponent={
<ToolTextInput title="Input Text" value={input} onChange={setInput} />
<ToolTextInput
title={t('string.quote.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult title={t('string.quote.resultTitle')} value={result} />
}
resultComponent={<ToolTextResult title="Quoted Text" value={result} />}
initialValues={initialValues}
getGroups={getGroups}
toolInfo={{
title: 'Text Quoter',
description:
"This tool allows you to add quotes around text. You can choose different quote characters, handle multi-line text, and control how empty lines are processed. It's useful for preparing text for programming, formatting data, or creating stylized text."
title: t('string.quote.toolInfo.title'),
description: t('string.quote.toolInfo.description')
}}
exampleCards={exampleCards}
input={input}

View File

@@ -5,10 +5,15 @@ import { lazy } from 'react';
export const tool = defineTool('string', {
name: 'Quote',
path: 'quote',
icon: 'proicons:quote',
icon: 'material-symbols-light:format-quote',
description:
'A tool to add quotation marks or custom characters around text. Perfect for formatting strings for code, citations, or stylistic purposes.',
shortDescription: 'Add quotes around text easily.',
"World's simplest browser-based utility for adding quotes to text. Input your text and instantly add various quote styles around it. Perfect for formatting text, creating citations, or adding emphasis to specific content.",
shortDescription: 'Add quotes around text with various styles',
keywords: ['quote'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'string.quote.name',
description: 'string.quote.description',
shortDescription: 'string.quote.shortDescription'
}
});

View File

@@ -5,10 +5,15 @@ import { lazy } from 'react';
export const tool = defineTool('string', {
name: 'Randomize case',
path: 'randomize-case',
icon: 'material-symbols-light:format-textdirection-l-to-r',
icon: 'material-symbols-light:shuffle',
description:
"World's simplest browser-based utility for randomizing the case of text. Just paste your text and get it instantly transformed with random uppercase and lowercase letters. Perfect for creating playful text styles, meme text, or simulating chaotic writing.",
shortDescription: 'Convert text to random uppercase and lowercase letters',
"World's simplest browser-based utility for randomizing text case. Input your text and instantly transform it with random upper and lower case letters. Perfect for creating unique text effects, testing case sensitivity, or generating varied text patterns.",
shortDescription: 'Randomize the case of letters in text',
keywords: ['randomize', 'case'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'string.randomizeCase.name',
description: 'string.randomizeCase.description',
shortDescription: 'string.randomizeCase.shortDescription'
}
});

View File

@@ -9,5 +9,10 @@ export const tool = defineTool('string', {
"Load your text in the input form on the left and you'll instantly get text with no duplicate lines in the output area. Powerful, free, and fast. Load text lines get unique text lines",
shortDescription: 'Quickly delete all repeated lines from text',
keywords: ['remove', 'duplicate', 'lines'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'string.removeDuplicateLines.name',
description: 'string.removeDuplicateLines.description',
shortDescription: 'string.removeDuplicateLines.shortDescription'
}
});

View File

@@ -9,6 +9,7 @@ import { initialValues, InitialValuesType } from './initialValues';
import ToolContent from '@components/ToolContent';
import { CardExampleType } from '@components/examples/ToolExamples';
import { ToolComponentProps } from '@tools/defineTool';
import { useTranslation } from 'react-i18next';
const exampleCards: CardExampleType<InitialValuesType>[] = [
{
@@ -48,6 +49,7 @@ const exampleCards: CardExampleType<InitialValuesType>[] = [
];
export default function Replacer({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -60,12 +62,12 @@ export default function Replacer({ title }: ToolComponentProps) {
updateField
}) => [
{
title: 'Text Repetitions',
title: t('string.repeat.textRepetitions'),
component: (
<Box>
<TextFieldWithDesc
description={'Number of repetitions.'}
placeholder="Number"
description={t('string.repeat.repeatAmountDescription')}
placeholder={t('string.repeat.numberPlaceholder')}
value={values.repeatAmount}
onOwnChange={(val) => updateField('repeatAmount', val)}
type={'number'}
@@ -74,12 +76,12 @@ export default function Replacer({ title }: ToolComponentProps) {
)
},
{
title: 'Repetitions Delimiter',
title: t('string.repeat.repetitionsDelimiter'),
component: (
<Box>
<TextFieldWithDesc
description={'Delimiter for output copies.'}
placeholder="Delimiter"
description={t('string.repeat.delimiterDescription')}
placeholder={t('string.repeat.delimiterPlaceholder')}
value={values.delimiter}
onOwnChange={(val) => updateField('delimiter', val)}
type={'text'}
@@ -98,15 +100,18 @@ export default function Replacer({ title }: ToolComponentProps) {
input={input}
setInput={setInput}
inputComponent={
<ToolTextInput title={'Input text'} value={input} onChange={setInput} />
<ToolTextInput
title={t('string.repeat.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult title={'Repeated text'} value={result} />
<ToolTextResult title={t('string.repeat.resultTitle')} value={result} />
}
toolInfo={{
title: 'Repeat text',
description:
'This tool allows you to repeat a given text multiple times with an optional separator.'
title: t('string.repeat.toolInfo.title'),
description: t('string.repeat.toolInfo.description')
}}
exampleCards={exampleCards}
/>

View File

@@ -9,5 +9,10 @@ export const tool = defineTool('string', {
description:
'This tool allows you to repeat a given text multiple times with an optional separator.',
keywords: ['text', 'repeat'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'string.repeat.name',
description: 'string.repeat.description',
shortDescription: 'string.repeat.shortDescription'
}
});

View File

@@ -12,6 +12,7 @@ import ToolExamples, {
import { ToolComponentProps } from '@tools/defineTool';
import { FormikProps } from 'formik';
import ToolContent from '@components/ToolContent';
import { useTranslation } from 'react-i18next';
const initialValues = {
multiLine: true,
@@ -58,6 +59,7 @@ const exampleCards: CardExampleType<typeof initialValues>[] = [
];
export default function Reverse({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -74,27 +76,27 @@ export default function Reverse({ title }: ToolComponentProps) {
updateField
}) => [
{
title: 'Reversal options',
title: t('string.reverse.reversalOptions'),
component: [
<CheckboxWithDesc
key="multiLine"
checked={values.multiLine}
title="Process multi-line text"
description="Each line will be reversed independently"
title={t('string.reverse.processMultiLine')}
description={t('string.reverse.processMultiLineDescription')}
onChange={(val) => updateField('multiLine', val)}
/>,
<CheckboxWithDesc
key="emptyItems"
checked={values.emptyItems}
title="Skip empty lines"
description="Empty lines will be removed from the output"
title={t('string.reverse.skipEmptyLines')}
description={t('string.reverse.skipEmptyLinesDescription')}
onChange={(val) => updateField('emptyItems', val)}
/>,
<CheckboxWithDesc
key="trim"
checked={values.trim}
title="Trim whitespace"
description="Remove leading and trailing whitespace from each line"
title={t('string.reverse.trimWhitespace')}
description={t('string.reverse.trimWhitespaceDescription')}
onChange={(val) => updateField('trim', val)}
/>
]
@@ -109,9 +111,18 @@ export default function Reverse({ title }: ToolComponentProps) {
compute={computeExternal}
input={input}
setInput={setInput}
inputComponent={<ToolTextInput value={input} onChange={setInput} />}
inputComponent={
<ToolTextInput
title={t('string.reverse.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult title={'Reversed text'} value={result} />
<ToolTextResult
title={t('string.reverse.resultTitle')}
value={result}
/>
}
exampleCards={exampleCards}
/>

View File

@@ -9,5 +9,10 @@ export const tool = defineTool('string', {
"World's simplest browser-based utility for reversing text. Input any text and get it instantly reversed, character by character. Perfect for creating mirror text, analyzing palindromes, or playing with text patterns. Preserves spaces and special characters while reversing.",
shortDescription: 'Reverse any text character by character',
keywords: ['reverse'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'string.reverse.name',
description: 'string.reverse.description',
shortDescription: 'string.reverse.shortDescription'
}
});

View File

@@ -5,6 +5,7 @@ import ToolTextResult from '@components/result/ToolTextResult';
import { rot13 } from './service';
import { CardExampleType } from '@components/examples/ToolExamples';
import { ToolComponentProps } from '@tools/defineTool';
import { useTranslation } from 'react-i18next';
type InitialValuesType = Record<string, never>;
@@ -30,6 +31,7 @@ const exampleCards: CardExampleType<InitialValuesType>[] = [
];
export default function Rot13({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -41,15 +43,20 @@ export default function Rot13({ title }: ToolComponentProps) {
<ToolContent
title={title}
inputComponent={
<ToolTextInput title="Input Text" value={input} onChange={setInput} />
<ToolTextInput
title={t('string.rot13.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult title={t('string.rot13.resultTitle')} value={result} />
}
resultComponent={<ToolTextResult title="ROT13 Result" value={result} />}
initialValues={initialValues}
getGroups={null}
toolInfo={{
title: 'What Is ROT13?',
description:
'ROT13 (rotate by 13 places) is a simple letter substitution cipher that replaces a letter with the 13th letter after it in the alphabet. ROT13 is a special case of the Caesar cipher which was developed in ancient Rome. Because there are 26 letters in the English alphabet, ROT13 is its own inverse; that is, to undo ROT13, the same algorithm is applied, so the same action can be used for encoding and decoding.'
title: t('string.rot13.toolInfo.title'),
description: t('string.rot13.toolInfo.description')
}}
exampleCards={exampleCards}
input={input}

View File

@@ -10,6 +10,7 @@ import { GetGroupsType } from '@components/options/ToolOptions';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import SimpleRadio from '@components/options/SimpleRadio';
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
import { useTranslation } from 'react-i18next';
interface InitialValuesType {
step: string;
@@ -63,6 +64,7 @@ const exampleCards: CardExampleType<InitialValuesType>[] = [
];
export default function Rotate({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -79,29 +81,29 @@ export default function Rotate({ title }: ToolComponentProps) {
updateField
}) => [
{
title: 'Rotation Options',
title: t('string.rotate.rotationOptions'),
component: (
<Box>
<TextFieldWithDesc
value={values.step}
onOwnChange={(val) => updateField('step', val)}
description={'Number of positions to rotate'}
description={t('string.rotate.stepDescription')}
type="number"
/>
<SimpleRadio
onClick={() => updateField('direction', 'right')}
checked={values.direction === 'right'}
title={'Rotate Right'}
title={t('string.rotate.rotateRight')}
/>
<SimpleRadio
onClick={() => updateField('direction', 'left')}
checked={values.direction === 'left'}
title={'Rotate Left'}
title={t('string.rotate.rotateLeft')}
/>
<CheckboxWithDesc
checked={values.multiLine}
onChange={(checked) => updateField('multiLine', checked)}
title={'Process as multi-line text (rotate each line separately)'}
title={t('string.rotate.processAsMultiLine')}
/>
</Box>
)
@@ -112,15 +114,20 @@ export default function Rotate({ title }: ToolComponentProps) {
<ToolContent
title={title}
inputComponent={
<ToolTextInput title="Input Text" value={input} onChange={setInput} />
<ToolTextInput
title={t('string.rotate.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult title={t('string.rotate.resultTitle')} value={result} />
}
resultComponent={<ToolTextResult title="Rotated Text" value={result} />}
initialValues={initialValues}
getGroups={getGroups}
toolInfo={{
title: 'String Rotation',
description:
'This tool allows you to rotate characters in a string by a specified number of positions. You can rotate to the left or right, and process multi-line text by rotating each line separately. String rotation is useful for simple text transformations, creating patterns, or implementing basic encryption techniques.'
title: t('string.rotate.toolInfo.title'),
description: t('string.rotate.toolInfo.description')
}}
exampleCards={exampleCards}
input={input}

View File

@@ -11,6 +11,7 @@ import ToolExamples, {
import { ToolComponentProps } from '@tools/defineTool';
import { FormikProps } from 'formik';
import ToolContent from '@components/ToolContent';
import { useTranslation } from 'react-i18next';
const initialValues = {
splitSeparatorType: 'symbol' as SplitOperatorType,
@@ -132,6 +133,7 @@ easy`,
];
export default function SplitText({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -170,18 +172,20 @@ export default function SplitText({ title }: ToolComponentProps) {
title={title}
input={input}
inputComponent={<ToolTextInput value={input} onChange={setInput} />}
resultComponent={<ToolTextResult title={'Text pieces'} value={result} />}
resultComponent={
<ToolTextResult title={t('string.split.resultTitle')} value={result} />
}
initialValues={initialValues}
getGroups={({ values, updateField }) => [
{
title: 'Split separator options',
title: t('string.split.splitSeparatorOptions'),
component: splitOperators.map(({ title, description, type }) => (
<RadioWithTextField
key={type}
checked={type === values.splitSeparatorType}
title={title}
title={t(`string.split.${type}Title`)}
fieldName={'splitSeparatorType'}
description={description}
description={t(`string.split.${type}Description`)}
value={values[`${type}Value`]}
onRadioClick={() => updateField('splitSeparatorType', type)}
onTextChange={(val) => updateField(`${type}Value`, val)}
@@ -189,13 +193,13 @@ export default function SplitText({ title }: ToolComponentProps) {
))
},
{
title: 'Output separator options',
title: t('string.split.outputSeparatorOptions'),
component: outputOptions.map((option) => (
<TextFieldWithDesc
key={option.accessor}
value={values[option.accessor]}
onOwnChange={(value) => updateField(option.accessor, value)}
description={option.description}
description={t(`string.split.${option.accessor}Description`)}
/>
))
}

View File

@@ -2,13 +2,17 @@ import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('string', {
name: 'Split',
path: 'split',
name: 'Text splitter',
icon: 'material-symbols-light:arrow-split',
icon: 'material-symbols-light:call-split',
description:
"World's simplest browser-based utility for splitting text. Load your text in the input form on the left and you'll automatically get pieces of this text on the right. Powerful, free, and fast. Load text get chunks.",
shortDescription: 'Quickly split a text',
longDescription: 'Quickly split a text',
keywords: ['text', 'split'],
component: lazy(() => import('./index'))
"World's simplest browser-based utility for splitting text. Input your text and specify a separator to split it into multiple parts. Perfect for data processing, text manipulation, or extracting specific content from larger text blocks.",
shortDescription: 'Split text into multiple parts using a separator',
keywords: ['split'],
component: lazy(() => import('./index')),
i18n: {
name: 'string.split.name',
description: 'string.split.description',
shortDescription: 'string.split.shortDescription'
}
});

View File

@@ -10,6 +10,7 @@ import { CardExampleType } from '@components/examples/ToolExamples';
import { ToolComponentProps } from '@tools/defineTool';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
import { useTranslation } from 'react-i18next';
const initialValues: InitialValuesType = {
emptyLines: false,
@@ -216,6 +217,7 @@ export default function Truncate({
title,
longDescription
}: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -228,49 +230,47 @@ export default function Truncate({
updateField
}) => [
{
title: 'Delimiters Options',
title: t('string.statistic.delimitersOptions'),
component: (
<Box>
<TextFieldWithDesc
value={values.sentenceDelimiters}
onOwnChange={(val) => updateField('sentenceDelimiters', val)}
placeholder="e.g. ., !, ?, ..."
description={
'Enter custom characters used to delimit sentences in your language (separated by comma) or leave it blank for default.'
}
placeholder={t('string.statistic.sentenceDelimitersPlaceholder')}
description={t('string.statistic.sentenceDelimitersDescription')}
/>
<TextFieldWithDesc
value={values.wordDelimiters}
onOwnChange={(val) => updateField('wordDelimiters', val)}
placeholder="eg. \\s.,;:!?\”«»()…"
description={
'Enter custom Regex to count Words or leave it blank for default.'
}
placeholder={t('string.statistic.wordDelimitersPlaceholder')}
description={t('string.statistic.wordDelimitersDescription')}
/>
</Box>
)
},
{
title: 'Statistics Options',
title: t('string.statistic.statisticsOptions'),
component: (
<Box>
<CheckboxWithDesc
checked={values.wordCount}
onChange={(value) => updateField('wordCount', value)}
title="Word Frequency Analysis"
description="Count how often each word appears in the text"
title={t('string.statistic.wordFrequencyAnalysis')}
description={t('string.statistic.wordFrequencyAnalysisDescription')}
/>
<CheckboxWithDesc
checked={values.characterCount}
onChange={(value) => updateField('characterCount', value)}
title="Character Frequency Analysis"
description="Count how often each character appears in the text"
title={t('string.statistic.characterFrequencyAnalysis')}
description={t(
'string.statistic.characterFrequencyAnalysisDescription'
)}
/>
<CheckboxWithDesc
checked={values.emptyLines}
onChange={(value) => updateField('emptyLines', value)}
title="Include Empty Lines"
description="Include blank lines when counting lines"
title={t('string.statistic.includeEmptyLines')}
description={t('string.statistic.includeEmptyLinesDescription')}
/>
</Box>
)
@@ -286,12 +286,22 @@ export default function Truncate({
input={input}
setInput={setInput}
inputComponent={
<ToolTextInput title={'Input text'} value={input} onChange={setInput} />
<ToolTextInput
title={t('string.statistic.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult title={'Text Statistics'} value={result} />
<ToolTextResult
title={t('string.statistic.resultTitle')}
value={result}
/>
}
toolInfo={{ title: `What is a ${title}?`, description: longDescription }}
toolInfo={{
title: t('string.statistic.toolInfo.title', { title }),
description: longDescription
}}
exampleCards={exampleCards}
/>
);

View File

@@ -11,5 +11,11 @@ export const tool = defineTool('string', {
longDescription:
'This tool provides various statistics about the text you input, including the number of lines, words, and characters. You can also choose to include empty lines in the count. it can count words and characters based on custom delimiters, allowing for flexible text analysis. Additionally, it can provide frequency statistics for words and characters, helping you understand the distribution of terms in your text.',
keywords: ['text', 'statistics', 'count', 'lines', 'words', 'characters'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'string.statistic.name',
description: 'string.statistic.description',
shortDescription: 'string.statistic.shortDescription',
longDescription: 'string.statistic.longDescription'
}
});

View File

@@ -10,6 +10,7 @@ import { initialValues, InitialValuesType } from './initialValues';
import ToolContent from '@components/ToolContent';
import { CardExampleType } from '@components/examples/ToolExamples';
import { ToolComponentProps } from '@tools/defineTool';
import { useTranslation } from 'react-i18next';
const exampleCards: CardExampleType<InitialValuesType>[] = [
{
@@ -60,6 +61,7 @@ const exampleCards: CardExampleType<InitialValuesType>[] = [
];
export default function Replacer({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -72,16 +74,16 @@ export default function Replacer({ title }: ToolComponentProps) {
updateField
}) => [
{
title: 'Search text',
title: t('string.textReplacer.searchText'),
component: (
<Box>
<SimpleRadio
onClick={() => updateField('mode', 'text')}
checked={values.mode === 'text'}
title={'Find This Pattern in Text'}
title={t('string.textReplacer.findPatternInText')}
/>
<TextFieldWithDesc
description={'Enter the text pattern that you want to replace.'}
description={t('string.textReplacer.searchPatternDescription')}
value={values.searchValue}
onOwnChange={(val) => updateField('searchValue', val)}
type={'text'}
@@ -89,12 +91,10 @@ export default function Replacer({ title }: ToolComponentProps) {
<SimpleRadio
onClick={() => updateField('mode', 'regexp')}
checked={values.mode === 'regexp'}
title={'Find a Pattern Using a RegExp'}
title={t('string.textReplacer.findPatternUsingRegexp')}
/>
<TextFieldWithDesc
description={
'Enter the regular expression that you want to replace.'
}
description={t('string.textReplacer.regexpDescription')}
value={values.searchRegexp}
onOwnChange={(val) => updateField('searchRegexp', val)}
type={'text'}
@@ -103,12 +103,12 @@ export default function Replacer({ title }: ToolComponentProps) {
)
},
{
title: 'Replace Text',
title: t('string.textReplacer.replaceText'),
component: (
<Box>
<TextFieldWithDesc
description={'Enter the pattern to use for replacement.'}
placeholder={'New text'}
description={t('string.textReplacer.replacePatternDescription')}
placeholder={t('string.textReplacer.newTextPlaceholder')}
value={values.replaceValue}
onOwnChange={(val) => updateField('replaceValue', val)}
type={'text'}
@@ -128,18 +128,20 @@ export default function Replacer({ title }: ToolComponentProps) {
setInput={setInput}
inputComponent={
<ToolTextInput
title="Text to replace"
title={t('string.textReplacer.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult title={'Text with replacements'} value={result} />
<ToolTextResult
title={t('string.textReplacer.resultTitle')}
value={result}
/>
}
toolInfo={{
title: 'Text Replacer',
description:
'Easily replace specific text in your content with this simple, browser-based tool. Just input your text, set the text you want to replace and the replacement value, and instantly get the updated version.'
title: t('string.textReplacer.toolInfo.title'),
description: t('string.textReplacer.toolInfo.description')
}}
exampleCards={exampleCards}
/>

View File

@@ -4,6 +4,7 @@ import ToolTextInput from '@components/input/ToolTextInput';
import ToolTextResult from '@components/result/ToolTextResult';
import { compute } from './service';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import { useTranslation } from 'react-i18next';
const initialValues = {
dotSymbol: '.',
@@ -11,6 +12,7 @@ const initialValues = {
};
export default function ToMorse() {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
const computeOptions = (optionsValues: typeof initialValues, input: any) => {
@@ -20,33 +22,34 @@ export default function ToMorse() {
return (
<ToolContent
title="To Morse"
title={t('string.toMorse.title')}
initialValues={initialValues}
compute={computeOptions}
input={input}
setInput={setInput}
inputComponent={<ToolTextInput value={input} onChange={setInput} />}
resultComponent={<ToolTextResult title={'Morse code'} value={result} />}
resultComponent={
<ToolTextResult
title={t('string.toMorse.resultTitle')}
value={result}
/>
}
getGroups={({ values, updateField }) => [
{
title: 'Short Signal',
title: t('string.toMorse.shortSignal'),
component: (
<TextFieldWithDesc
description={
'Symbol that will correspond to the dot in Morse code.'
}
description={t('string.toMorse.dotSymbolDescription')}
value={values.dotSymbol}
onOwnChange={(val) => updateField('dotSymbol', val)}
/>
)
},
{
title: 'Long Signal',
title: t('string.toMorse.longSignal'),
component: (
<TextFieldWithDesc
description={
'Symbol that will correspond to the dash in Morse code.'
}
description={t('string.toMorse.dashSymbolDescription')}
value={values.dashSymbol}
onOwnChange={(val) => updateField('dashSymbol', val)}
/>

View File

@@ -10,5 +10,10 @@ export const tool = defineTool('string', {
"World's simplest browser-based utility for converting text to Morse code. Load your text in the input form on the left and you'll instantly get Morse code in the output area. Powerful, free, and fast. Load text get Morse code.",
shortDescription: 'Quickly encode text to morse',
keywords: ['to', 'morse'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'string.toMorse.name',
description: 'string.toMorse.description',
shortDescription: 'string.toMorse.shortDescription'
}
});

View File

@@ -11,6 +11,7 @@ import { CardExampleType } from '@components/examples/ToolExamples';
import { ToolComponentProps } from '@tools/defineTool';
import SimpleRadio from '@components/options/SimpleRadio';
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
import { useTranslation } from 'react-i18next';
const exampleCards: CardExampleType<InitialValuesType>[] = [
{
@@ -67,6 +68,7 @@ Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
];
export default function Truncate({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -79,31 +81,31 @@ export default function Truncate({ title }: ToolComponentProps) {
updateField
}) => [
{
title: 'Truncation Side',
title: t('string.truncate.truncationSide'),
component: (
<Box>
<SimpleRadio
onClick={() => updateField('truncationSide', 'right')}
checked={values.truncationSide === 'right'}
title={'Right-side Truncation'}
description={'Remove characters from the end of the text.'}
title={t('string.truncate.rightSideTruncation')}
description={t('string.truncate.rightSideDescription')}
/>
<SimpleRadio
onClick={() => updateField('truncationSide', 'left')}
checked={values.truncationSide === 'left'}
title={'Left-side Truncation'}
description={'Remove characters from the start of the text.'}
title={t('string.truncate.leftSideTruncation')}
description={t('string.truncate.leftSideDescription')}
/>
</Box>
)
},
{
title: 'Length and Lines',
title: t('string.truncate.lengthAndLines'),
component: (
<Box>
<TextFieldWithDesc
description={'Number of characters to leave in the text.'}
placeholder="Number"
description={t('string.truncate.maxLengthDescription')}
placeholder={t('string.truncate.numberPlaceholder')}
value={values.maxLength}
onOwnChange={(val) => updateField('maxLength', val)}
type={'number'}
@@ -111,27 +113,25 @@ export default function Truncate({ title }: ToolComponentProps) {
<CheckboxWithDesc
onChange={(val) => updateField('lineByLine', val)}
checked={values.lineByLine}
title={'Line-by-line Truncating'}
description={'Truncate each line separately.'}
title={t('string.truncate.lineByLineTruncating')}
description={t('string.truncate.lineByLineDescription')}
/>
</Box>
)
},
{
title: 'Suffix and Affix',
title: t('string.truncate.suffixAndAffix'),
component: (
<Box>
<CheckboxWithDesc
onChange={(val) => updateField('addIndicator', val)}
checked={values.addIndicator}
title={'Add Truncation Indicator'}
title={t('string.truncate.addTruncationIndicator')}
description={''}
/>
<TextFieldWithDesc
description={
'Characters to add at the end (or start) of the text. Note: They count towards the length.'
}
placeholder="Characters"
description={t('string.truncate.indicatorDescription')}
placeholder={t('string.truncate.charactersPlaceholder')}
value={values.indicator}
onOwnChange={(val) => updateField('indicator', val)}
type={'text'}
@@ -150,15 +150,21 @@ export default function Truncate({ title }: ToolComponentProps) {
input={input}
setInput={setInput}
inputComponent={
<ToolTextInput title={'Input text'} value={input} onChange={setInput} />
<ToolTextInput
title={t('string.truncate.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult title={'Truncated text'} value={result} />
<ToolTextResult
title={t('string.truncate.resultTitle')}
value={result}
/>
}
toolInfo={{
title: 'Truncate text',
description:
'Load your text in the input form on the left and you will automatically get truncated text on the right.'
title: t('string.truncate.toolInfo.title'),
description: t('string.truncate.toolInfo.description')
}}
exampleCards={exampleCards}
/>

View File

@@ -2,12 +2,17 @@ import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('string', {
name: 'Truncate text',
name: 'Truncate',
path: 'truncate',
shortDescription: 'Truncate your text easily',
icon: 'material-symbols-light:short-text',
icon: 'material-symbols-light:content-cut',
description:
'Load your text in the input form on the left and you will automatically get truncated text on the right.',
keywords: ['text', 'truncate'],
component: lazy(() => import('./index'))
"World's simplest browser-based utility for truncating text. Input your text and specify the maximum length to cut it down. Perfect for data processing, text formatting, or limiting content length.",
shortDescription: 'Truncate text to a specified length',
keywords: ['truncate'],
component: lazy(() => import('./index')),
i18n: {
name: 'string.truncate.name',
description: 'string.truncate.description',
shortDescription: 'string.truncate.shortDescription'
}
});

View File

@@ -5,6 +5,7 @@ import { UppercaseInput } from './service';
import { CardExampleType } from '@components/examples/ToolExamples';
import { ToolComponentProps } from '@tools/defineTool';
import ToolContent from '@components/ToolContent';
import { useTranslation } from 'react-i18next';
const initialValues = {};
@@ -35,6 +36,7 @@ const exampleCards: CardExampleType<typeof initialValues>[] = [
];
export default function Uppercase({ title }: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -53,9 +55,18 @@ export default function Uppercase({ title }: ToolComponentProps) {
compute={computeExternal}
input={input}
setInput={setInput}
inputComponent={<ToolTextInput value={input} onChange={setInput} />}
inputComponent={
<ToolTextInput
title={t('string.uppercase.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult title={'Uppercase text'} value={result} />
<ToolTextResult
title={t('string.uppercase.resultTitle')}
value={result}
/>
}
exampleCards={exampleCards}
/>

View File

@@ -4,10 +4,15 @@ import { lazy } from 'react';
export const tool = defineTool('string', {
name: 'Uppercase',
path: 'uppercase',
icon: 'material-symbols-light:text-fields',
icon: 'material-symbols-light:format-textdirection-l-to-r',
description:
"World's simplest browser-based utility for converting text to uppercase. Just input your text and it will be automatically converted to all capital letters. Perfect for creating headlines, emphasizing text, or standardizing text format. Supports various text formats and preserves special characters.",
shortDescription: 'Convert text to uppercase letters',
"World's simplest browser-based utility for converting text to uppercase. Input your text and instantly get all characters in uppercase. Perfect for formatting, shouting, or emphasizing text.",
shortDescription: 'Convert text to uppercase',
keywords: ['uppercase'],
component: lazy(() => import('./index'))
component: lazy(() => import('./index')),
i18n: {
name: 'string.uppercase.name',
description: 'string.uppercase.description',
shortDescription: 'string.uppercase.shortDescription'
}
});

View File

@@ -6,6 +6,7 @@ import ToolTextResult from '@components/result/ToolTextResult';
import { GetGroupsType } from '@components/options/ToolOptions';
import { CardExampleType } from '@components/examples/ToolExamples';
import { checkLeapYear } from './service';
import { useTranslation } from 'react-i18next';
const initialValues = {};
@@ -56,6 +57,7 @@ export default function ConvertDaysToHours({
title,
longDescription
}: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -75,7 +77,10 @@ export default function ConvertDaysToHours({
getGroups={getGroups}
setInput={setInput}
compute={compute}
toolInfo={{ title: `What is a ${title}?`, description: longDescription }}
toolInfo={{
title: t('time.checkLeapYears.toolInfo.title', { title }),
description: longDescription
}}
exampleCards={exampleCards}
/>
);

View File

@@ -2,13 +2,17 @@ import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('time', {
path: 'check-leap-years',
name: 'Check Leap Years',
icon: 'arcticons:calendar-simple-29',
path: 'check-leap-years',
icon: 'material-symbols:calendar-month',
description:
' You can check if a given calendar year is a leap year. You can enter one or many different years into the input field with one date per line and get the answer to the test question of whether the given year is a leap year.',
shortDescription: 'Convert days to hours easily.',
keywords: ['check', 'leap', 'years'],
longDescription: `This is a quick online utility for testing if the given year is a leap year. Just as a reminder, a leap year has 366 days, which is one more day than a common year. This extra day is added to the month of February and it falls on February 29th. There's a simple mathematical formula for calculating if the given year is a leap year. Leap years are those years that are divisible by 4 but not divisible by 100, as well as years that are divisible by 100 and 400 simultaneously. Our algorithm checks each input year using this formula and outputs the year's status. For example, if you enter the value "2025" as input, the program will display "2025 is not a leap year.", and for the value "2028", the status will be "2028 is a leap year.". You can also enter multiple years as the input in a column and get a matching column of statuses as the output.`,
component: lazy(() => import('./index'))
'Check if a year is a leap year. Enter a year to determine if it has 366 days instead of 365.',
shortDescription: 'Check if a year is a leap year',
keywords: ['leap', 'year', 'calendar', 'date'],
component: lazy(() => import('./index')),
i18n: {
name: 'time.checkLeapYears.name',
description: 'time.checkLeapYears.description',
shortDescription: 'time.checkLeapYears.shortDescription'
}
});

View File

@@ -8,6 +8,7 @@ import { GetGroupsType } from '@components/options/ToolOptions';
import { CardExampleType } from '@components/examples/ToolExamples';
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
import { convertDaysToHours } from './service';
import { useTranslation } from 'react-i18next';
const initialValues = {
hoursFlag: false
@@ -62,6 +63,7 @@ export default function ConvertDaysToHours({
title,
longDescription
}: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -74,14 +76,14 @@ export default function ConvertDaysToHours({
updateField
}) => [
{
title: 'Hours Name',
title: t('time.convertDaysToHours.hoursName'),
component: (
<Box>
<CheckboxWithDesc
onChange={(val) => updateField('hoursFlag', val)}
checked={values.hoursFlag}
title={'Add Hours Name'}
description={'Append the string hours to output values'}
title={t('time.convertDaysToHours.addHoursName')}
description={t('time.convertDaysToHours.addHoursNameDescription')}
/>
</Box>
)
@@ -98,7 +100,10 @@ export default function ConvertDaysToHours({
getGroups={getGroups}
setInput={setInput}
compute={compute}
toolInfo={{ title: `What is a ${title}?`, description: longDescription }}
toolInfo={{
title: t('time.convertDaysToHours.toolInfo.title'),
description: t('time.convertDaysToHours.toolInfo.description')
}}
exampleCards={exampleCards}
/>
);

View File

@@ -2,14 +2,17 @@ import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('time', {
path: 'convert-days-to-hours',
name: 'Convert Days to Hours',
icon: 'ri:24-hours-line',
path: 'convert-days-to-hours',
icon: 'material-symbols:schedule',
description:
'With this browser-based application, you can calculate how many hours there are in the given number of days. The application takes the input values (days), multiplies them by 24 and that converts them into hours. It supports both integer and decimal day values and it can convert multiple values at the same time.',
shortDescription: 'Convert days to hours easily.',
keywords: ['convert', 'days', 'hours'],
longDescription:
'This is a quick online utility for converting days to hours. One day is 24 hours and to convert days to hours, we simply do the multiplication operation: hours = days × 24. For example, 2 days is 2 × 24 = 48 hours and 5 days is 5 × 24 = 120 hours. You can convert not only full days to hours but also fractional day values. For example, 1.5 days is 1.5 × 24 = 36 hours and 4.6 days is 4.6 × 24 = 110.4 hours. You can enter multiple days in the input field (one value per line). In this case, they will all be computed in parallel and at once. The program also supports the postfix "days" or "d" for the input values and you can add the postfix "hours" to the output values. Timeabulous!',
component: lazy(() => import('./index'))
'Convert days to hours. Enter the number of days to get the equivalent number of hours.',
shortDescription: 'Convert days to hours',
keywords: ['days', 'hours', 'convert', 'time'],
component: lazy(() => import('./index')),
i18n: {
name: 'time.convertDaysToHours.name',
description: 'time.convertDaysToHours.description',
shortDescription: 'time.convertDaysToHours.shortDescription'
}
});

View File

@@ -2,14 +2,17 @@ import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('time', {
path: 'convert-hours-to-days',
name: 'Convert Hours to Days',
icon: 'mdi:hours-24',
path: 'convert-hours-to-days',
icon: 'material-symbols:schedule',
description:
'With this browser-based application, you can calculate how many days there are in the given number of hours. Given one or more hour values in the input, it converts them into days via the simple math formula: days = hours/24. It works with arbitrary large hour values and you can also customize the decimal day precision.',
shortDescription: 'Convert hours to days easily.',
keywords: ['convert', 'hours', 'days'],
longDescription:
"This is a quick online utility for converting hours to days. To figure out the number of days in the specified hours, the program divides them by 24. For example, if the input hours value is 48, then by doing 48/24, it finds that there are 2 days, or if the hours value is 120, then it's 120/24 = 5 days. If the hours value is not divisible by 24, then the number of days is displayed as a decimal number. For example, 36 hours is 36/24 = 1.5 days and 100 hours is approximately 4.167 days. You can specify the precision of the decimal fraction calculation in the options. You can also enable the option that adds the postfix 'days' to all the output values. Timeabulous!",
component: lazy(() => import('./index'))
'Convert hours to days. Enter the number of hours to get the equivalent number of days.',
shortDescription: 'Convert hours to days',
keywords: ['hours', 'days', 'convert', 'time'],
component: lazy(() => import('./index')),
i18n: {
name: 'time.convertHoursToDays.name',
description: 'time.convertHoursToDays.description',
shortDescription: 'time.convertHoursToDays.shortDescription'
}
});

View File

@@ -8,6 +8,7 @@ import { GetGroupsType } from '@components/options/ToolOptions';
import { CardExampleType } from '@components/examples/ToolExamples';
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
import { convertSecondsToTime } from './service';
import { useTranslation } from 'react-i18next';
const initialValues = {
paddingFlag: false
@@ -68,6 +69,7 @@ export default function SecondsToTime({
title,
longDescription
}: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -80,14 +82,14 @@ export default function SecondsToTime({
updateField
}) => [
{
title: 'Time Padding',
title: t('time.convertSecondsToTime.timePadding'),
component: (
<Box>
<CheckboxWithDesc
onChange={(val) => updateField('paddingFlag', val)}
checked={values.paddingFlag}
title={'Add Padding'}
description={'Add zero padding to hours, minutes, and seconds.'}
title={t('time.convertSecondsToTime.addPadding')}
description={t('time.convertSecondsToTime.addPaddingDescription')}
/>
</Box>
)
@@ -104,7 +106,10 @@ export default function SecondsToTime({
getGroups={getGroups}
setInput={setInput}
compute={compute}
toolInfo={{ title: `What is a ${title}?`, description: longDescription }}
toolInfo={{
title: t('time.convertSecondsToTime.toolInfo.title', { title }),
description: longDescription
}}
exampleCards={exampleCards}
/>
);

View File

@@ -2,14 +2,17 @@ import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('time', {
path: 'convert-seconds-to-time',
name: 'Convert Seconds to Time',
icon: 'fluent-mdl2:time-picker',
path: 'convert-seconds-to-time',
icon: 'material-symbols:schedule',
description:
'With this browser-based application, you can convert seconds to clock time. Given the seconds input value, it converts them into full hours (H), minutes (M), and seconds (S) and prints them in human-readable clock format (H:M:S or HH:MM:SS) in the output field.',
shortDescription: 'Quicky convert seconds to clock time in H:M:S format.',
keywords: ['convert', 'seconds', 'time', 'clock'],
longDescription:
'This is a quick online utility for converting seconds to H:M:S or HH:MM:SS digital clock time format. It calculates the number of full hours, full minutes, and remaining seconds from the input seconds and outputs regular clock time. For example, 100 seconds is 1 minute and 40 seconds so we get the clock time 00:01:40. To convert seconds to human-readable time we use the Euclidean division algorithm, also known as a division with remainder. If "n" is the input seconds value, then the hours "h" are calculated from the formula n = 3600×h + r, where r is the remainder of dividing n by 3600. Minutes "m" are calculated from the formula r = 60×m + s, and seconds "s" is the remainder of dividing r by 60. For example, if the input n = 4000, then 4000 = 3600×1 + 400. From here we find that the full hours value is 1. Next, the remaining 400 seconds are equal to 60×6 + 40. From here, there are 6 full minutes and 40 more remaining seconds. Thus, we find that 4000 seconds in human time 1 hour, 6 minutes, and 40 seconds. By default, the program outputs the clock time in a padded HH:MM:SS format (i.e. 01:06:40) but you can also disable the padding option and get just H:M:S (i.e. 1:6:40). Timeabulous!',
component: lazy(() => import('./index'))
'Convert seconds to a readable time format (hours:minutes:seconds). Enter the number of seconds to get the formatted time.',
shortDescription: 'Convert seconds to time format',
keywords: ['seconds', 'time', 'convert', 'format'],
component: lazy(() => import('./index')),
i18n: {
name: 'time.convertSecondsToTime.name',
description: 'time.convertSecondsToTime.description',
shortDescription: 'time.convertSecondsToTime.shortDescription'
}
});

View File

@@ -5,6 +5,7 @@ import ToolTextInput from '@components/input/ToolTextInput';
import ToolTextResult from '@components/result/ToolTextResult';
import { CardExampleType } from '@components/examples/ToolExamples';
import { convertTimetoSeconds } from './service';
import { useTranslation } from 'react-i18next';
const initialValues = {};
type InitialValuesType = typeof initialValues;
@@ -75,6 +76,7 @@ export default function TimeToSeconds({
title,
longDescription
}: ToolComponentProps) {
const { t } = useTranslation();
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
@@ -92,7 +94,10 @@ export default function TimeToSeconds({
getGroups={null}
setInput={setInput}
compute={compute}
toolInfo={{ title: `What is a ${title}?`, description: longDescription }}
toolInfo={{
title: t('time.convertTimeToSeconds.toolInfo.title', { title }),
description: longDescription
}}
exampleCards={exampleCards}
/>
);

View File

@@ -2,14 +2,17 @@ import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('time', {
path: 'convert-time-to-seconds',
name: 'Convert Time to Seconds',
icon: 'ic:round-timer-10-select',
path: 'convert-time-to-seconds',
icon: 'material-symbols:schedule',
description:
'With this browser-based application, you can convert clock time provided in hours, minutes, and seconds into just seconds. Given a time in HH:MM:SS format, it calculates HH*3600 + MM*60 + SS and prints this value in the output box. It supports AM/PM time formats as well as clock times beyond 24 hours.',
shortDescription: 'Quickly convert clock time in H:M:S format to seconds.',
keywords: ['convert', 'seconds', 'time', 'clock'],
longDescription:
'This is a quick online utility for calculating how many seconds there are in the given time. When you input a full clock time in the input box (in format H:M:S), it gets split into hours, minutes, and seconds, and using the math formula hours×60×60 plus minutes×60 plus seconds, it finds the seconds. If seconds are missing (format is H:M), then the formula becomes hours×60×60 plus minutes×60. If minutes are also missing, then the formula becomes hours×60×60. As an extra feature, hours, minutes, and seconds are not limited to just 24 hours, 60 minutes, and 60 seconds. You can use any hours value, any minutes value, and any seconds value. For example, the input time "72:00:00" will find the number of seconds in three days (72 hours is 3×24 hours) and the input time "0:1000:0" will find seconds in 1000 minutes. Timeabulous!',
component: lazy(() => import('./index'))
'Convert time format (hours:minutes:seconds) to total seconds. Enter time in HH:MM:SS format to get the total seconds.',
shortDescription: 'Convert time format to seconds',
keywords: ['time', 'seconds', 'convert', 'format'],
component: lazy(() => import('./index')),
i18n: {
name: 'time.convertTimeToSeconds.name',
description: 'time.convertTimeToSeconds.description',
shortDescription: 'time.convertTimeToSeconds.shortDescription'
}
});

View File

@@ -2,23 +2,17 @@ import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('time', {
name: 'Crontab explainer',
name: 'Crontab Guru',
path: 'crontab-guru',
icon: 'mdi:calendar-clock',
icon: 'material-symbols:schedule',
description:
'Parse, validate, and explain crontab expressions in plain English.',
shortDescription: 'Crontab expression parser and explainer',
keywords: [
'crontab',
'cron',
'schedule',
'guru',
'time',
'expression',
'parser',
'explain'
],
longDescription:
'Enter a crontab expression (like "35 16 * * 0-5") to get a human-readable explanation and validation. Useful for understanding and debugging cron schedules. Inspired by crontab.guru.',
component: lazy(() => import('./index'))
'Generate and understand cron expressions. Create cron schedules for automated tasks and system jobs.',
shortDescription: 'Generate and understand cron expressions',
keywords: ['cron', 'schedule', 'automation', 'expression'],
component: lazy(() => import('./index')),
i18n: {
name: 'time.crontabGuru.name',
description: 'time.crontabGuru.description',
shortDescription: 'time.crontabGuru.shortDescription'
}
});

Some files were not shown because too many files have changed in this diff Show More