- Sorry, this page does not seem to exist
+ {{ $t('404.sorry') }}
- Maybe the cache is doing tricky things, try force-refreshing?
+ {{ $t('404.maybe') }}
- Back home
+ {{ $t('404.backHome') }}
diff --git a/src/pages/About.vue b/src/pages/About.vue
index 3fada35e..556f0f9e 100644
--- a/src/pages/About.vue
+++ b/src/pages/About.vue
@@ -1,85 +1,9 @@
-
- About
-
- This wonderful website, made with ❤ by
-
- Corentin Thomasset
- ,
- aggregates useful tools for developer and people working in IT. If you find it useful, please feel free to share
- it to people you think may find it useful too and don't forget to bookmark it in your shortcut bar!
-
-
- IT Tools is open-source (under the MIT license) and free, and will always be, but it costs me money to host and
- renew the domain name. If you want to support my work, and encourage me to add more tools, please consider
- supporting by
- tracker.trackEvent({ eventName: 'Support button clicked' })"
- >
- sponsoring me
- .
-
-
- Technologies
-
- IT Tools is made in Vue.js (Vue 3) with the the Naive UI component library and is hosted and continuously deployed
- by Vercel. Third-party open-source libraries are used in some tools, you may find the complete list in the
-
- package.json
-
- file of the repository.
-
-
- Found a bug? A tool is missing?
-
- If you need a tool that is currently not present here, and you think can be useful, you are welcome to submit a
- feature request in the
-
- issues section
-
- in the GitHub repository.
-
-
- And if you found a bug, or something doesn't work as expected, please file a bug report in the
-
- issues section
-
- in the GitHub repository.
-
-
- With a concrete example, if you wash 3 plates in 5 minutes and you have 500 plates to wash, it will take you 5
- hours and 10 minutes to wash them all.
+ With a concrete example, if you wash 5 plates in 3 minutes and you have 500 plates to wash, it will take you 5
+ hours to wash them all.
diff --git a/src/tools/eta-calculator/index.ts b/src/tools/eta-calculator/index.ts
index abda2870..5016ab66 100644
--- a/src/tools/eta-calculator/index.ts
+++ b/src/tools/eta-calculator/index.ts
@@ -1,11 +1,11 @@
import { Hourglass } from '@vicons/tabler';
import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
- name: 'ETA calculator',
+ name: translate('tools.eta-calculator.title'),
path: '/eta-calculator',
- description:
- 'An ETA (Estimated Time of Arrival) calculator to know the approximate end time of a task, for example the moment of ending of a download.',
+ description: translate('tools.eta-calculator.description'),
keywords: ['eta', 'calculator', 'estimated', 'time', 'arrival', 'average'],
component: () => import('./eta-calculator.vue'),
icon: Hourglass,
diff --git a/src/tools/git-memo/index.ts b/src/tools/git-memo/index.ts
index c91ee813..f65ffe07 100644
--- a/src/tools/git-memo/index.ts
+++ b/src/tools/git-memo/index.ts
@@ -1,11 +1,11 @@
import { BrandGit } from '@vicons/tabler';
import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
- name: 'Git cheatsheet',
+ name: translate('tools.git-memo.title'),
path: '/git-memo',
- description:
- 'Git is a decentralized version management software. With this cheatsheet you will have a quick access to the most common git commands.',
+ description: translate('tools.git-memo.description'),
keywords: ['git', 'push', 'force', 'pull', 'commit', 'amend', 'rebase', 'merge', 'reset', 'soft', 'hard', 'lease'],
component: () => import('./git-memo.vue'),
icon: BrandGit,
diff --git a/src/tools/hash-text/hash-text.service.ts b/src/tools/hash-text/hash-text.service.ts
index 6b9b0148..d0bc4bde 100644
--- a/src/tools/hash-text/hash-text.service.ts
+++ b/src/tools/hash-text/hash-text.service.ts
@@ -2,6 +2,6 @@ export function convertHexToBin(hex: string) {
return hex
.trim()
.split('')
- .map(byte => parseInt(byte, 16).toString(2).padStart(4, '0'))
+ .map(byte => Number.parseInt(byte, 16).toString(2).padStart(4, '0'))
.join('');
}
diff --git a/src/tools/hash-text/index.ts b/src/tools/hash-text/index.ts
index 3012747c..2070e41d 100644
--- a/src/tools/hash-text/index.ts
+++ b/src/tools/hash-text/index.ts
@@ -1,11 +1,11 @@
import { EyeOff } from '@vicons/tabler';
import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
- name: 'Hash text',
+ name: translate('tools.hash-text.title'),
path: '/hash-text',
- description:
- 'Hash a text string using the function you need : MD5, SHA1, SHA256, SHA224, SHA512, SHA384, SHA3 or RIPEMD160',
+ description: translate('tools.hash-text.description'),
keywords: [
'hash',
'digest',
diff --git a/src/tools/hmac-generator/index.ts b/src/tools/hmac-generator/index.ts
index c0ca7da4..3500684e 100644
--- a/src/tools/hmac-generator/index.ts
+++ b/src/tools/hmac-generator/index.ts
@@ -1,11 +1,11 @@
import { ShortTextRound } from '@vicons/material';
import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
- name: 'Hmac generator',
+ name: translate('tools.hmac-generator.title'),
path: '/hmac-generator',
- description:
- 'Computes a hash-based message authentication code (HMAC) using a secret key and your favorite hashing function.',
+ description: translate('tools.hmac-generator.description'),
keywords: ['hmac', 'generator', 'MD5', 'SHA1', 'SHA256', 'SHA224', 'SHA512', 'SHA384', 'SHA3', 'RIPEMD160'],
component: () => import('./hmac-generator.vue'),
icon: ShortTextRound,
diff --git a/src/tools/html-entities/index.ts b/src/tools/html-entities/index.ts
index 4907dc68..e292f087 100644
--- a/src/tools/html-entities/index.ts
+++ b/src/tools/html-entities/index.ts
@@ -1,10 +1,11 @@
import { Code } from '@vicons/tabler';
import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
- name: 'Escape html entities',
+ name: translate('tools.html-entities.title'),
path: '/html-entities',
- description: 'Escape or unescape html entities (replace <,>, &, " and \' to their html version)',
+ description: translate('tools.html-entities.description'),
keywords: ['html', 'entities', 'escape', 'unescape', 'special', 'characters', 'tags'],
component: () => import('./html-entities.vue'),
icon: Code,
diff --git a/src/tools/html-wysiwyg-editor/editor/menu-bar-item.vue b/src/tools/html-wysiwyg-editor/editor/menu-bar-item.vue
index 9a4cf1bd..5be23292 100644
--- a/src/tools/html-wysiwyg-editor/editor/menu-bar-item.vue
+++ b/src/tools/html-wysiwyg-editor/editor/menu-bar-item.vue
@@ -6,13 +6,9 @@ const { icon, title, action, isActive } = toRefs(props);
-
-
-
-
-
-
-
- {{ title }}
-
+
+
+
+
+
diff --git a/src/tools/html-wysiwyg-editor/editor/menu-bar.vue b/src/tools/html-wysiwyg-editor/editor/menu-bar.vue
index d3ad3168..9069673c 100644
--- a/src/tools/html-wysiwyg-editor/editor/menu-bar.vue
+++ b/src/tools/html-wysiwyg-editor/editor/menu-bar.vue
@@ -84,8 +84,8 @@ const items: MenuItem[] = [
type: 'button',
icon: H3,
title: 'Heading 3',
- action: () => editor.value.chain().focus().toggleHeading({ level: 4 }).run(),
- isActive: () => editor.value.isActive('heading', { level: 4 }),
+ action: () => editor.value.chain().focus().toggleHeading({ level: 3 }).run(),
+ isActive: () => editor.value.isActive('heading', { level: 3 }),
},
{
type: 'button',
diff --git a/src/tools/html-wysiwyg-editor/html-wysiwyg-editor.vue b/src/tools/html-wysiwyg-editor/html-wysiwyg-editor.vue
index 69238215..e6187c9b 100644
--- a/src/tools/html-wysiwyg-editor/html-wysiwyg-editor.vue
+++ b/src/tools/html-wysiwyg-editor/html-wysiwyg-editor.vue
@@ -1,14 +1,16 @@
-
+
diff --git a/src/tools/html-wysiwyg-editor/index.ts b/src/tools/html-wysiwyg-editor/index.ts
index 461ad235..3a2ab007 100644
--- a/src/tools/html-wysiwyg-editor/index.ts
+++ b/src/tools/html-wysiwyg-editor/index.ts
@@ -1,10 +1,11 @@
import { Edit } from '@vicons/tabler';
import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
- name: 'HTML WYSIWYG editor',
+ name: translate('tools.html-wysiwyg-editor.title'),
path: '/html-wysiwyg-editor',
- description: 'Online HTML editor with feature-rich WYSIWYG editor, get the source code of the content immediately.',
+ description: translate('tools.html-wysiwyg-editor.description'),
keywords: ['html', 'wysiwyg', 'editor', 'p', 'ul', 'ol', 'converter', 'live'],
component: () => import('./html-wysiwyg-editor.vue'),
icon: Edit,
diff --git a/src/tools/http-status-codes/index.ts b/src/tools/http-status-codes/index.ts
index 43afae83..b3138943 100644
--- a/src/tools/http-status-codes/index.ts
+++ b/src/tools/http-status-codes/index.ts
@@ -2,11 +2,12 @@ import { HttpRound } from '@vicons/material';
import { defineTool } from '../tool';
import { codesByCategories } from './http-status-codes.constants';
+import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
- name: 'HTTP status codes',
+ name: translate('tools.http-status-codes.title'),
path: '/http-status-codes',
- description: 'The list of all HTTP status codes their name and their meaning.',
+ description: translate('tools.http-status-codes.description'),
keywords: [
'http',
'status',
diff --git a/src/tools/iban-validator-and-parser/iban-validator-and-parser.e2e.spec.ts b/src/tools/iban-validator-and-parser/iban-validator-and-parser.e2e.spec.ts
new file mode 100644
index 00000000..c4a99860
--- /dev/null
+++ b/src/tools/iban-validator-and-parser/iban-validator-and-parser.e2e.spec.ts
@@ -0,0 +1,52 @@
+import { type Page, expect, test } from '@playwright/test';
+
+async function extractIbanInfo({ page }: { page: Page }) {
+ const itemsLines = await page
+ .locator('.c-key-value-list__item').all();
+
+ return await Promise.all(
+ itemsLines.map(async item => [
+ (await item.locator('.c-key-value-list__key').textContent() ?? '').trim(),
+ (await item.locator('.c-key-value-list__value').textContent() ?? '').trim(),
+ ]),
+ );
+}
+
+test.describe('Tool - Iban validator and parser', () => {
+ test.beforeEach(async ({ page }) => {
+ await page.goto('/iban-validator-and-parser');
+ });
+
+ test('Has correct title', async ({ page }) => {
+ await expect(page).toHaveTitle('IBAN validator and parser - IT Tools');
+ });
+
+ test('iban info are extracted from a valid iban', async ({ page }) => {
+ await page.getByTestId('iban-input').fill('DE89370400440532013000');
+
+ const ibanInfo = await extractIbanInfo({ page });
+
+ expect(ibanInfo).toEqual([
+ ['Is IBAN valid ?', 'Yes'],
+ ['Is IBAN a QR-IBAN ?', 'No'],
+ ['Country code', 'DE'],
+ ['BBAN', '370400440532013000'],
+ ['IBAN friendly format', 'DE89 3704 0044 0532 0130 00'],
+ ]);
+ });
+
+ test('invalid iban errors are displayed', async ({ page }) => {
+ await page.getByTestId('iban-input').fill('FR7630006060011234567890189');
+
+ const ibanInfo = await extractIbanInfo({ page });
+
+ expect(ibanInfo).toEqual([
+ ['Is IBAN valid ?', 'No'],
+ ['IBAN errors', 'Wrong account bank branch checksum Wrong IBAN checksum'],
+ ['Is IBAN a QR-IBAN ?', 'No'],
+ ['Country code', 'N/A'],
+ ['BBAN', 'N/A'],
+ ['IBAN friendly format', 'FR76 3000 6060 0112 3456 7890 189'],
+ ]);
+ });
+});
diff --git a/src/tools/iban-validator-and-parser/iban-validator-and-parser.service.ts b/src/tools/iban-validator-and-parser/iban-validator-and-parser.service.ts
new file mode 100644
index 00000000..bde71dba
--- /dev/null
+++ b/src/tools/iban-validator-and-parser/iban-validator-and-parser.service.ts
@@ -0,0 +1,18 @@
+import { ValidationErrorsIBAN } from 'ibantools';
+
+export { getFriendlyErrors };
+
+const ibanErrorToMessage = {
+ [ValidationErrorsIBAN.NoIBANProvided]: 'No IBAN provided',
+ [ValidationErrorsIBAN.NoIBANCountry]: 'No IBAN country',
+ [ValidationErrorsIBAN.WrongBBANLength]: 'Wrong BBAN length',
+ [ValidationErrorsIBAN.WrongBBANFormat]: 'Wrong BBAN format',
+ [ValidationErrorsIBAN.ChecksumNotNumber]: 'Checksum is not a number',
+ [ValidationErrorsIBAN.WrongIBANChecksum]: 'Wrong IBAN checksum',
+ [ValidationErrorsIBAN.WrongAccountBankBranchChecksum]: 'Wrong account bank branch checksum',
+ [ValidationErrorsIBAN.QRIBANNotAllowed]: 'QR-IBAN not allowed',
+};
+
+function getFriendlyErrors(errorCodes: ValidationErrorsIBAN[]) {
+ return errorCodes.map(errorCode => ibanErrorToMessage[errorCode]).filter(Boolean);
+}
diff --git a/src/tools/iban-validator-and-parser/iban-validator-and-parser.vue b/src/tools/iban-validator-and-parser/iban-validator-and-parser.vue
new file mode 100644
index 00000000..6844dc5a
--- /dev/null
+++ b/src/tools/iban-validator-and-parser/iban-validator-and-parser.vue
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/tools/iban-validator-and-parser/index.ts b/src/tools/iban-validator-and-parser/index.ts
new file mode 100644
index 00000000..ff7ff135
--- /dev/null
+++ b/src/tools/iban-validator-and-parser/index.ts
@@ -0,0 +1,13 @@
+import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
+import Bank from '~icons/mdi/bank';
+
+export const tool = defineTool({
+ name: translate('tools.iban-validator-and-parser.title'),
+ path: '/iban-validator-and-parser',
+ description: translate('tools.iban-validator-and-parser.description'),
+ keywords: ['iban', 'validator', 'and', 'parser', 'bic', 'bank'],
+ component: () => import('./iban-validator-and-parser.vue'),
+ icon: Bank,
+ createdAt: new Date('2023-08-26'),
+});
diff --git a/src/tools/index.ts b/src/tools/index.ts
index 0cfc1739..388cfaf4 100644
--- a/src/tools/index.ts
+++ b/src/tools/index.ts
@@ -1,6 +1,25 @@
import { tool as base64FileConverter } from './base64-file-converter';
import { tool as base64StringConverter } from './base64-string-converter';
import { tool as basicAuthGenerator } from './basic-auth-generator';
+import { tool as emailNormalizer } from './email-normalizer';
+
+import { tool as asciiTextDrawer } from './ascii-text-drawer';
+
+import { tool as textToUnicode } from './text-to-unicode';
+import { tool as safelinkDecoder } from './safelink-decoder';
+import { tool as xmlToJson } from './xml-to-json';
+import { tool as jsonToXml } from './json-to-xml';
+import { tool as regexTester } from './regex-tester';
+import { tool as regexMemo } from './regex-memo';
+import { tool as markdownToHtml } from './markdown-to-html';
+import { tool as pdfSignatureChecker } from './pdf-signature-checker';
+import { tool as numeronymGenerator } from './numeronym-generator';
+import { tool as macAddressGenerator } from './mac-address-generator';
+import { tool as textToBinary } from './text-to-binary';
+import { tool as ulidGenerator } from './ulid-generator';
+import { tool as ibanValidatorAndParser } from './iban-validator-and-parser';
+import { tool as stringObfuscator } from './string-obfuscator';
+import { tool as textDiff } from './text-diff';
import { tool as emojiPicker } from './emoji-picker';
import { tool as passwordStrengthAnalyser } from './password-strength-analyser';
import { tool as yamlToToml } from './yaml-to-toml';
@@ -53,6 +72,7 @@ import { tool as metaTagGenerator } from './meta-tag-generator';
import { tool as mimeTypes } from './mime-types';
import { tool as otpCodeGeneratorAndValidator } from './otp-code-generator-and-validator';
import { tool as qrCodeGenerator } from './qr-code-generator';
+import { tool as wifiQrCodeGenerator } from './wifi-qr-code-generator';
import { tool as randomPortGenerator } from './random-port-generator';
import { tool as romanNumeralConverter } from './roman-numeral-converter';
import { tool as sqlPrettify } from './sql-prettify';
@@ -66,11 +86,12 @@ import { tool as urlParser } from './url-parser';
import { tool as uuidGenerator } from './uuid-generator';
import { tool as macAddressLookup } from './mac-address-lookup';
import { tool as xmlFormatter } from './xml-formatter';
+import { tool as yamlViewer } from './yaml-viewer';
export const toolsByCategory: ToolCategory[] = [
{
name: 'Crypto',
- components: [tokenGenerator, hashText, bcrypt, uuidGenerator, cypher, bip39, hmacGenerator, rsaKeyPairGenerator, passwordStrengthAnalyser],
+ components: [tokenGenerator, hashText, bcrypt, uuidGenerator, ulidGenerator, cypher, bip39, hmacGenerator, rsaKeyPairGenerator, passwordStrengthAnalyser, pdfSignatureChecker],
},
{
name: 'Converter',
@@ -83,6 +104,8 @@ export const toolsByCategory: ToolCategory[] = [
colorConverter,
caseConverter,
textToNatoAlphabet,
+ textToBinary,
+ textToUnicode,
yamlToJson,
yamlToToml,
jsonToYaml,
@@ -90,6 +113,9 @@ export const toolsByCategory: ToolCategory[] = [
listConverter,
tomlToJson,
tomlToYaml,
+ xmlToJson,
+ jsonToXml,
+ markdownToHtml,
],
},
{
@@ -110,11 +136,12 @@ export const toolsByCategory: ToolCategory[] = [
userAgentParser,
httpStatusCodes,
jsonDiff,
+ safelinkDecoder,
],
},
{
name: 'Images and videos',
- components: [qrCodeGenerator, svgPlaceholderGenerator, cameraRecorder],
+ components: [qrCodeGenerator, wifiQrCodeGenerator, svgPlaceholderGenerator, cameraRecorder],
},
{
name: 'Development',
@@ -129,11 +156,15 @@ export const toolsByCategory: ToolCategory[] = [
chmodCalculator,
dockerRunToDockerComposeConverter,
xmlFormatter,
+ yamlViewer,
+ emailNormalizer,
+ regexTester,
+ regexMemo,
],
},
{
name: 'Network',
- components: [ipv4SubnetCalculator, ipv4AddressConverter, ipv4RangeExpander, macAddressLookup, ipv6UlaGenerator],
+ components: [ipv4SubnetCalculator, ipv4AddressConverter, ipv4RangeExpander, macAddressLookup, macAddressGenerator, ipv6UlaGenerator],
},
{
name: 'Math',
@@ -145,11 +176,19 @@ export const toolsByCategory: ToolCategory[] = [
},
{
name: 'Text',
- components: [loremIpsumGenerator, textStatistics, emojiPicker],
+ components: [
+ loremIpsumGenerator,
+ textStatistics,
+ emojiPicker,
+ stringObfuscator,
+ textDiff,
+ numeronymGenerator,
+ asciiTextDrawer,
+ ],
},
{
name: 'Data',
- components: [phoneParserAndFormatter],
+ components: [phoneParserAndFormatter, ibanValidatorAndParser],
},
];
diff --git a/src/tools/integer-base-converter/index.ts b/src/tools/integer-base-converter/index.ts
index 0008568c..f60d996d 100644
--- a/src/tools/integer-base-converter/index.ts
+++ b/src/tools/integer-base-converter/index.ts
@@ -1,10 +1,11 @@
import { ArrowsLeftRight } from '@vicons/tabler';
import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
- name: 'Integer base converter',
+ name: translate('tools.base-converter.title'),
path: '/base-converter',
- description: 'Convert number between different bases (decimal, hexadecimal, binary, octal, base64, ...)',
+ description: translate('tools.base-converter.description'),
keywords: ['integer', 'number', 'base', 'conversion', 'decimal', 'hexadecimal', 'binary', 'octal', 'base64'],
component: () => import('./integer-base-converter.vue'),
icon: ArrowsLeftRight,
diff --git a/src/tools/integer-base-converter/integer-base-converter.model.test.ts b/src/tools/integer-base-converter/integer-base-converter.model.test.ts
index d0387b64..c7d7db79 100644
--- a/src/tools/integer-base-converter/integer-base-converter.model.test.ts
+++ b/src/tools/integer-base-converter/integer-base-converter.model.test.ts
@@ -11,6 +11,9 @@ describe('integer-base-converter', () => {
expect(convertBase({ value: '10100101', fromBase: 2, toBase: 16 })).toEqual('a5');
expect(convertBase({ value: '192654', fromBase: 10, toBase: 8 })).toEqual('570216');
expect(convertBase({ value: 'zz', fromBase: 64, toBase: 10 })).toEqual('2275');
+ expect(convertBase({ value: '42540766411283223938465490632011909384', fromBase: 10, toBase: 10 })).toEqual('42540766411283223938465490632011909384');
+ expect(convertBase({ value: '42540766411283223938465490632011909384', fromBase: 10, toBase: 16 })).toEqual('20010db8000085a300000000ac1f8908');
+ expect(convertBase({ value: '20010db8000085a300000000ac1f8908', fromBase: 16, toBase: 10 })).toEqual('42540766411283223938465490632011909384');
});
});
});
diff --git a/src/tools/integer-base-converter/integer-base-converter.model.ts b/src/tools/integer-base-converter/integer-base-converter.model.ts
index b4470e57..da0fe77f 100644
--- a/src/tools/integer-base-converter/integer-base-converter.model.ts
+++ b/src/tools/integer-base-converter/integer-base-converter.model.ts
@@ -5,16 +5,16 @@ export function convertBase({ value, fromBase, toBase }: { value: string; fromBa
let decValue = value
.split('')
.reverse()
- .reduce((carry: number, digit: string, index: number) => {
+ .reduce((carry: bigint, digit: string, index: number) => {
if (!fromRange.includes(digit)) {
throw new Error(`Invalid digit "${digit}" for base ${fromBase}.`);
}
- return (carry += fromRange.indexOf(digit) * fromBase ** index);
- }, 0);
+ return (carry += BigInt(fromRange.indexOf(digit)) * BigInt(fromBase) ** BigInt(index));
+ }, 0n);
let newValue = '';
while (decValue > 0) {
- newValue = toRange[decValue % toBase] + newValue;
- decValue = (decValue - (decValue % toBase)) / toBase;
+ newValue = toRange[Number(decValue % BigInt(toBase))] + newValue;
+ decValue = (decValue - (decValue % BigInt(toBase))) / BigInt(toBase);
}
return newValue || '0';
}
diff --git a/src/tools/ipv4-address-converter/index.ts b/src/tools/ipv4-address-converter/index.ts
index 62d2daf1..66ae03a3 100644
--- a/src/tools/ipv4-address-converter/index.ts
+++ b/src/tools/ipv4-address-converter/index.ts
@@ -1,10 +1,11 @@
import { Binary } from '@vicons/tabler';
import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
- name: 'Ipv4 address converter',
+ name: translate('tools.ipv4-address-converter.title'),
path: '/ipv4-address-converter',
- description: 'Convert an ip address into decimal, binary, hexadecimal or event in ipv6',
+ description: translate('tools.ipv4-address-converter.description'),
keywords: ['ipv4', 'address', 'converter', 'decimal', 'hexadecimal', 'binary', 'ipv6'],
component: () => import('./ipv4-address-converter.vue'),
icon: Binary,
diff --git a/src/tools/ipv4-address-converter/ipv4-address-converter.service.ts b/src/tools/ipv4-address-converter/ipv4-address-converter.service.ts
index 1ef487eb..903ff5bb 100644
--- a/src/tools/ipv4-address-converter/ipv4-address-converter.service.ts
+++ b/src/tools/ipv4-address-converter/ipv4-address-converter.service.ts
@@ -23,7 +23,7 @@ function ipv4ToIpv6({ ip, prefix = '0000:0000:0000:0000:0000:ffff:' }: { ip: str
+ _.chain(ip)
.trim()
.split('.')
- .map(part => parseInt(part).toString(16).padStart(2, '0'))
+ .map(part => Number.parseInt(part).toString(16).padStart(2, '0'))
.chunk(2)
.map(blocks => blocks.join(''))
.join(':')
diff --git a/src/tools/ipv4-range-expander/index.ts b/src/tools/ipv4-range-expander/index.ts
index 233f7cc4..49dfae95 100644
--- a/src/tools/ipv4-range-expander/index.ts
+++ b/src/tools/ipv4-range-expander/index.ts
@@ -1,11 +1,11 @@
import { UnfoldMoreOutlined } from '@vicons/material';
import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
- name: 'IPv4 range expander',
+ name: translate('tools.ipv4-range-expander.title'),
path: '/ipv4-range-expander',
- description:
- 'Given a start and an end IPv4 address this tool calculates a valid IPv4 network with its CIDR notation.',
+ description: translate('tools.ipv4-range-expander.description'),
keywords: ['ipv4', 'range', 'expander', 'subnet', 'creator', 'cidr'],
component: () => import('./ipv4-range-expander.vue'),
icon: UnfoldMoreOutlined,
diff --git a/src/tools/ipv4-range-expander/ipv4-range-expander.service.ts b/src/tools/ipv4-range-expander/ipv4-range-expander.service.ts
index 78fbde5b..14761f59 100644
--- a/src/tools/ipv4-range-expander/ipv4-range-expander.service.ts
+++ b/src/tools/ipv4-range-expander/ipv4-range-expander.service.ts
@@ -13,7 +13,7 @@ function getRangesize(start: string, end: string) {
return -1;
}
- return 1 + parseInt(end, 2) - parseInt(start, 2);
+ return 1 + Number.parseInt(end, 2) - Number.parseInt(start, 2);
}
function getCidr(start: string, end: string) {
@@ -55,8 +55,8 @@ function calculateCidr({ startIp, endIp }: { startIp: string; endIp: string }) {
const cidr = getCidr(start, end);
if (cidr != null) {
const result: Ipv4RangeExpanderResult = {};
- result.newEnd = bits2ip(parseInt(cidr.end, 2));
- result.newStart = bits2ip(parseInt(cidr.start, 2));
+ result.newEnd = bits2ip(Number.parseInt(cidr.end, 2));
+ result.newStart = bits2ip(Number.parseInt(cidr.start, 2));
result.newCidr = `${result.newStart}/${cidr.mask}`;
result.newSize = getRangesize(cidr.start, cidr.end);
diff --git a/src/tools/ipv4-subnet-calculator/index.ts b/src/tools/ipv4-subnet-calculator/index.ts
index fb4bfb43..1bae7282 100644
--- a/src/tools/ipv4-subnet-calculator/index.ts
+++ b/src/tools/ipv4-subnet-calculator/index.ts
@@ -1,10 +1,11 @@
import { RouterOutlined } from '@vicons/material';
import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
- name: 'IPv4 subnet calculator',
+ name: translate('tools.ipv4-subnet-calculator.title'),
path: '/ipv4-subnet-calculator',
- description: 'Parse your IPv4 CIDR blocks and get all the info you need about your sub network.',
+ description: translate('tools.ipv4-subnet-calculator.description'),
keywords: ['ipv4', 'subnet', 'calculator', 'mask', 'network', 'cidr', 'netmask', 'bitmask', 'broadcast', 'address'],
component: () => import('./ipv4-subnet-calculator.vue'),
icon: RouterOutlined,
diff --git a/src/tools/ipv6-ula-generator/index.ts b/src/tools/ipv6-ula-generator/index.ts
index 24efaeba..51bfd6fc 100644
--- a/src/tools/ipv6-ula-generator/index.ts
+++ b/src/tools/ipv6-ula-generator/index.ts
@@ -1,10 +1,11 @@
import { BuildingFactory } from '@vicons/tabler';
import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
- name: 'IPv6 ULA generator',
+ name: translate('tools.ipv6-ula-generator.title'),
path: '/ipv6-ula-generator',
- description: 'Generate your own local, non-routable IP addresses on your network according to RFC4193.',
+ description: translate('tools.ipv6-ula-generator.description'),
keywords: ['ipv6', 'ula', 'generator', 'rfc4193', 'network', 'private'],
component: () => import('./ipv6-ula-generator.vue'),
icon: BuildingFactory,
diff --git a/src/tools/json-diff/index.ts b/src/tools/json-diff/index.ts
index 7c4c1eee..a4c0319c 100644
--- a/src/tools/json-diff/index.ts
+++ b/src/tools/json-diff/index.ts
@@ -1,10 +1,11 @@
import { CompareArrowsRound } from '@vicons/material';
import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
- name: 'JSON diff',
+ name: translate('tools.json-diff.title'),
path: '/json-diff',
- description: 'Compare two JSON objects and get the differences between them.',
+ description: translate('tools.json-diff.description'),
keywords: ['json', 'diff', 'compare', 'difference', 'object', 'data'],
component: () => import('./json-diff.vue'),
icon: CompareArrowsRound,
diff --git a/src/tools/json-minify/index.ts b/src/tools/json-minify/index.ts
index e6a02dbe..fbe5831b 100644
--- a/src/tools/json-minify/index.ts
+++ b/src/tools/json-minify/index.ts
@@ -1,10 +1,11 @@
import { Braces } from '@vicons/tabler';
import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
- name: 'JSON minify',
+ name: translate('tools.json-minify.title'),
path: '/json-minify',
- description: 'Minify and compress your JSON by removing unnecessary white spaces.',
+ description: translate('tools.json-minify.description'),
keywords: ['json', 'minify', 'format'],
component: () => import('./json-minify.vue'),
icon: Braces,
diff --git a/src/tools/json-to-csv/index.ts b/src/tools/json-to-csv/index.ts
index acfef02f..9f38b82f 100644
--- a/src/tools/json-to-csv/index.ts
+++ b/src/tools/json-to-csv/index.ts
@@ -1,10 +1,11 @@
import { List } from '@vicons/tabler';
import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
- name: 'JSON to CSV',
+ name: translate('tools.json-to-csv.title'),
path: '/json-to-csv',
- description: 'Convert JSON to CSV with automatic header detection.',
+ description: translate('tools.json-to-csv.description'),
keywords: ['json', 'to', 'csv', 'convert'],
component: () => import('./json-to-csv.vue'),
icon: List,
diff --git a/src/tools/json-to-toml/index.ts b/src/tools/json-to-toml/index.ts
index 13e45eaf..da42c18d 100644
--- a/src/tools/json-to-toml/index.ts
+++ b/src/tools/json-to-toml/index.ts
@@ -1,10 +1,11 @@
import { Braces } from '@vicons/tabler';
import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
- name: 'JSON to TOML',
+ name: translate('tools.json-to-toml.title'),
path: '/json-to-toml',
- description: 'Parse and convert JSON to TOML.',
+ description: translate('tools.json-to-toml.description'),
keywords: ['json', 'parse', 'toml', 'convert', 'transform'],
component: () => import('./json-to-toml.vue'),
icon: Braces,
diff --git a/src/tools/json-to-xml/index.ts b/src/tools/json-to-xml/index.ts
new file mode 100644
index 00000000..c35ace2b
--- /dev/null
+++ b/src/tools/json-to-xml/index.ts
@@ -0,0 +1,12 @@
+import { Braces } from '@vicons/tabler';
+import { defineTool } from '../tool';
+
+export const tool = defineTool({
+ name: 'JSON to XML',
+ path: '/json-to-xml',
+ description: 'Convert JSON to XML',
+ keywords: ['json', 'xml'],
+ component: () => import('./json-to-xml.vue'),
+ icon: Braces,
+ createdAt: new Date('2024-08-09'),
+});
diff --git a/src/tools/json-to-xml/json-to-xml.vue b/src/tools/json-to-xml/json-to-xml.vue
new file mode 100644
index 00000000..96a7cf16
--- /dev/null
+++ b/src/tools/json-to-xml/json-to-xml.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
diff --git a/src/tools/json-to-yaml-converter/index.ts b/src/tools/json-to-yaml-converter/index.ts
index 9db09d3e..c01e3ec0 100644
--- a/src/tools/json-to-yaml-converter/index.ts
+++ b/src/tools/json-to-yaml-converter/index.ts
@@ -1,10 +1,11 @@
import { Braces } from '@vicons/tabler';
import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
- name: 'JSON to YAML converter',
+ name: translate('tools.json-to-yaml-converter.title'),
path: '/json-to-yaml-converter',
- description: 'Simply convert JSON to YAML with this live online converter.',
+ description: translate('tools.json-to-yaml-converter.description'),
keywords: ['yaml', 'to', 'json'],
component: () => import('./json-to-yaml.vue'),
icon: Braces,
diff --git a/src/tools/json-viewer/index.ts b/src/tools/json-viewer/index.ts
index 6b5b8812..bc488245 100644
--- a/src/tools/json-viewer/index.ts
+++ b/src/tools/json-viewer/index.ts
@@ -1,10 +1,11 @@
import { Braces } from '@vicons/tabler';
import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
- name: 'JSON prettify and format',
+ name: translate('tools.json-prettify.title'),
path: '/json-prettify',
- description: 'Prettify your JSON string to a human friendly readable format.',
+ description: translate('tools.json-prettify.description'),
keywords: ['json', 'viewer', 'prettify', 'format'],
component: () => import('./json-viewer.vue'),
icon: Braces,
diff --git a/src/tools/jwt-parser/index.ts b/src/tools/jwt-parser/index.ts
index 7249ace0..939b4b34 100644
--- a/src/tools/jwt-parser/index.ts
+++ b/src/tools/jwt-parser/index.ts
@@ -1,10 +1,11 @@
import { Key } from '@vicons/tabler';
import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
- name: 'JWT parser',
+ name: translate('tools.jwt-parser.title'),
path: '/jwt-parser',
- description: 'Parse and decode your JSON Web Token (jwt) and display its content.',
+ description: translate('tools.jwt-parser.description'),
keywords: [
'jwt',
'parser',
diff --git a/src/tools/jwt-parser/jwt-parser.service.ts b/src/tools/jwt-parser/jwt-parser.service.ts
index 19edc5f2..543f4c8b 100644
--- a/src/tools/jwt-parser/jwt-parser.service.ts
+++ b/src/tools/jwt-parser/jwt-parser.service.ts
@@ -1,6 +1,5 @@
import jwtDecode, { type JwtHeader, type JwtPayload } from 'jwt-decode';
import _ from 'lodash';
-import { match } from 'ts-pattern';
import { ALGORITHM_DESCRIPTIONS, CLAIM_DESCRIPTIONS } from './jwt-parser.constants';
export { decodeJwt };
@@ -20,7 +19,7 @@ function decodeJwt({ jwt }: { jwt: string }) {
function parseClaims({ claim, value }: { claim: string; value: unknown }) {
const claimDescription = CLAIM_DESCRIPTIONS[claim];
- const formattedValue = _.isPlainObject(value) ? JSON.stringify(value, null, 3) : _.toString(value);
+ const formattedValue = _.isPlainObject(value) || _.isArray(value) ? JSON.stringify(value, null, 3) : _.toString(value);
const friendlyValue = getFriendlyValue({ claim, value });
return {
@@ -32,10 +31,15 @@ function parseClaims({ claim, value }: { claim: string; value: unknown }) {
}
function getFriendlyValue({ claim, value }: { claim: string; value: unknown }) {
- return match(claim)
- .with('exp', 'nbf', 'iat', () => dateFormatter(value))
- .with('alg', () => (_.isString(value) ? ALGORITHM_DESCRIPTIONS[value] : undefined))
- .otherwise(() => undefined);
+ if (['exp', 'nbf', 'iat'].includes(claim)) {
+ return dateFormatter(value);
+ }
+
+ if (claim === 'alg' && _.isString(value)) {
+ return ALGORITHM_DESCRIPTIONS[value];
+ }
+
+ return undefined;
}
function dateFormatter(value: unknown) {
diff --git a/src/tools/jwt-parser/jwt-parser.vue b/src/tools/jwt-parser/jwt-parser.vue
index 6b30fc0c..a26064d7 100644
--- a/src/tools/jwt-parser/jwt-parser.vue
+++ b/src/tools/jwt-parser/jwt-parser.vue
@@ -39,7 +39,7 @@ const validation = useValidation({
{{ section.title }}
+
diff --git a/src/tools/percentage-calculator/index.ts b/src/tools/percentage-calculator/index.ts
index 736f5706..33c5b2f1 100644
--- a/src/tools/percentage-calculator/index.ts
+++ b/src/tools/percentage-calculator/index.ts
@@ -1,10 +1,11 @@
import { Percentage } from '@vicons/tabler';
import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
- name: 'Percentage calculator',
+ name: translate('tools.percentage-calculator.title'),
path: '/percentage-calculator',
- description: 'Easily calculate percentages from a value to another value, or from a percentage to a value.',
+ description: translate('tools.percentage-calculator.description'),
keywords: ['percentage', 'calculator', 'calculate', 'value', 'number', '%'],
component: () => import('./percentage-calculator.vue'),
icon: Percentage,
diff --git a/src/tools/phone-parser-and-formatter/index.ts b/src/tools/phone-parser-and-formatter/index.ts
index 5b19ae61..094b21e8 100644
--- a/src/tools/phone-parser-and-formatter/index.ts
+++ b/src/tools/phone-parser-and-formatter/index.ts
@@ -1,11 +1,11 @@
import { Phone } from '@vicons/tabler';
import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
- name: 'Phone parser and formatter',
+ name: translate('tools.phone-parser-and-formatter.title'),
path: '/phone-parser-and-formatter',
- description:
- 'Parse, validate and format phone numbers. Get information about the phone number, like the country code, type, etc.',
+ description: translate('tools.phone-parser-and-formatter.description'),
keywords: [
'phone',
'parser',
diff --git a/src/tools/qr-code-generator/index.ts b/src/tools/qr-code-generator/index.ts
index 4c2f86bb..b97b4cbc 100644
--- a/src/tools/qr-code-generator/index.ts
+++ b/src/tools/qr-code-generator/index.ts
@@ -1,11 +1,11 @@
import { Qrcode } from '@vicons/tabler';
import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
- name: 'QR Code generator',
+ name: translate('tools.qrcode-generator.title'),
path: '/qrcode-generator',
- description:
- 'Generate and download QR-code for an url or just a text and customize the background and foreground colors.',
+ description: translate('tools.qrcode-generator.description'),
keywords: ['qr', 'code', 'generator', 'square', 'color', 'link', 'low', 'medium', 'quartile', 'high', 'transparent'],
component: () => import('./qr-code-generator.vue'),
icon: Qrcode,
diff --git a/src/tools/random-port-generator/index.ts b/src/tools/random-port-generator/index.ts
index febdc2a4..e300b8f0 100644
--- a/src/tools/random-port-generator/index.ts
+++ b/src/tools/random-port-generator/index.ts
@@ -1,10 +1,11 @@
import { Server } from '@vicons/tabler';
import { defineTool } from '../tool';
+import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
- name: 'Random port generator',
+ name: translate('tools.random-port-generator.title'),
path: '/random-port-generator',
- description: 'Generate random port numbers outside of the range of "known" ports (0-1023).',
+ description: translate('tools.random-port-generator.description'),
keywords: ['system', 'port', 'lan', 'generator', 'random', 'development', 'computer'],
component: () => import('./random-port-generator.vue'),
icon: Server,
diff --git a/src/tools/regex-memo/index.ts b/src/tools/regex-memo/index.ts
new file mode 100644
index 00000000..f1f56489
--- /dev/null
+++ b/src/tools/regex-memo/index.ts
@@ -0,0 +1,12 @@
+import { BrandJavascript } from '@vicons/tabler';
+import { defineTool } from '../tool';
+
+export const tool = defineTool({
+ name: 'Regex cheatsheet',
+ path: '/regex-memo',
+ description: 'Javascript Regex/Regular Expression cheatsheet',
+ keywords: ['regex', 'regular', 'expression', 'javascript', 'memo', 'cheatsheet'],
+ component: () => import('./regex-memo.vue'),
+ icon: BrandJavascript,
+ createdAt: new Date('2024-09-20'),
+});
diff --git a/src/tools/regex-memo/regex-memo.content.md b/src/tools/regex-memo/regex-memo.content.md
new file mode 100644
index 00000000..0f779401
--- /dev/null
+++ b/src/tools/regex-memo/regex-memo.content.md
@@ -0,0 +1,121 @@
+### Normal characters
+
+Expression | Description
+:--|:--
+`.` or `[^\n\r]` | any character *excluding* a newline or carriage return
+`[A-Za-z]` | alphabet
+`[a-z]` | lowercase alphabet
+`[A-Z]` | uppercase alphabet
+`\d` or `[0-9]` | digit
+`\D` or `[^0-9]` | non-digit
+`_` | underscore
+`\w` or `[A-Za-z0-9_]` | alphabet, digit or underscore
+`\W` or `[^A-Za-z0-9_]` | inverse of `\w`
+`\S` | inverse of `\s`
+
+### Whitespace characters
+
+Expression | Description
+:--|:--
+` ` | space
+`\t` | tab
+`\n` | newline
+`\r` | carriage return
+`\s` | space, tab, newline or carriage return
+
+### Character set
+
+Expression | Description
+:--|:--
+`[xyz]` | either `x`, `y` or `z`
+`[^xyz]` | neither `x`, `y` nor `z`
+`[1-3]` | either `1`, `2` or `3`
+`[^1-3]` | neither `1`, `2` nor `3`
+
+- Think of a character set as an `OR` operation on the single characters that are enclosed between the square brackets.
+- Use `^` after the opening `[` to “negate” the character set.
+- Within a character set, `.` means a literal period.
+
+### Characters that require escaping
+
+#### Outside a character set
+
+Expression | Description
+:--|:--
+`\.` | period
+`\^` | caret
+`\$` | dollar sign
+`\|` | pipe
+`\\` | back slash
+`\/` | forward slash
+`\(` | opening bracket
+`\)` | closing bracket
+`\[` | opening square bracket
+`\]` | closing square bracket
+`\{` | opening curly bracket
+`\}` | closing curly bracket
+
+#### Inside a character set
+
+Expression | Description
+:--|:--
+`\\` | back slash
+`\]` | closing square bracket
+
+- A `^` must be escaped only if it occurs immediately after the opening `[` of the character set.
+- A `-` must be escaped only if it occurs between two alphabets or two digits.
+
+### Quantifiers
+
+Expression | Description
+:--|:--
+`{2}` | exactly 2
+`{2,}` | at least 2
+`{2,7}` | at least 2 but no more than 7
+`*` | 0 or more
+`+` | 1 or more
+`?` | exactly 0 or 1
+
+- The quantifier goes *after* the expression to be quantified.
+
+### Boundaries
+
+Expression | Description
+:--|:--
+`^` | start of string
+`$` | end of string
+`\b` | word boundary
+
+- How word boundary matching works:
+ - At the beginning of the string if the first character is `\w`.
+ - Between two adjacent characters within the string, if the first character is `\w` and the second character is `\W`.
+ - At the end of the string if the last character is `\w`.
+
+### Matching
+
+Expression | Description
+:--|:--
+`foo\|bar` | match either `foo` or `bar`
+`foo(?=bar)` | match `foo` if it’s before `bar`
+`foo(?!bar)` | match `foo` if it’s *not* before `bar`
+`(?<=bar)foo` | match `foo` if it’s after `bar`
+`(?
+import { useThemeVars } from 'naive-ui';
+import Memo from './regex-memo.content.md';
+
+const themeVars = useThemeVars();
+
+
+
+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Magni reprehenderit itaque enim? Suscipit magni optio velit
quia, eveniet repellat pariatur quaerat laudantium dignissimos natus, beatae deleniti adipisci, atque necessitatibus
odio!
+
+
+ Lorem ipsum dolor sit amet consectetur adipisicing elit.
+ Molestias, quisquam vitae saepe dolores quas debitis ab r
+ ecusandae suscipit ex dignissimos minus quam repellat sunt.
+ Molestiae culpa blanditiis totam sapiente dignissimos.
+