This commit is contained in:
steffenrapp 2025-04-08 11:42:50 +02:00 committed by GitHub
commit 0d85581ef3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 368 additions and 102 deletions

View file

@ -1,16 +1,9 @@
'404':
notFound: 404 Nicht gefunden
sorry: Entschuldigung, diese Seite scheint nicht zu existieren
maybe: >-
Vielleicht macht der Cache etwas Seltsames. Mit einem erzwungenen Neuladen
versuchen?
backHome: Zurück zur Startseite
home:
categories:
newestTools: Neueste Tools
favoriteTools: Deine Lieblingstools
allTools: Alle Tools
favoritesDndToolTip: 'Ziehen und Ablegen, um Favoriten neu zu ordnen'
favoritesDndToolTip: Drag and Drop, um Favoriten neu zu ordnen
subtitle: Praktische Tools für Entwickler
toggleMenu: Menü umschalten
home: Startseite
@ -72,6 +65,13 @@ about:
funktioniert, melde bitte einen Fehler im
[Issues-Bereich](https://github.com/CorentinTh/it-tools/issues/new/choose)
im GitHub-Repository.
404:
notFound: 404 Nicht gefunden
sorry: Entschuldigung, diese Seite scheint nicht zu existieren
maybe: >-
Vielleicht macht der Cache etwas Seltsames. Mit einem erzwungenen Neuladen
versuchen?
backHome: Zurück zur Startseite
favoriteButton:
remove: Aus Favoriten entfernen
add: Zu Favoriten hinzufügen
@ -79,6 +79,20 @@ toolCard:
new: Neu
search:
label: Suche
placeholder: Tippe, um ein Tool oder einen Befehl zu suchen...
textareaCopyable:
copy: In die Zwischenablage kopieren
copied: Kopiert!
spanCopyable:
copy: In die Zwischenablage kopieren
copied: Kopiert!
inputCopyable:
copy: In die Zwischenablage kopieren
copied: Kopiert!
formatTransformer:
input: Eingabe
input-placeholder: Eingabe...
output: Ausgabe
tools:
categories:
favorite-tools: Deine Lieblingstools
@ -102,6 +116,10 @@ tools:
description: >-
Überwache die Dauer einer Sache. Im Grunde ein Chronometer mit einfachen
Chronometerfunktionen.
button:
start: Start
stop: Stopp
reset: Zurücksetzen
token-generator:
title: Token-Generator
description: >-
@ -296,6 +314,13 @@ tools:
description: >-
Generiere und downloade QR-Codes für eine URL oder einfach einen Text und
passe die Hintergrund- und Vordergrundfarben an.
text: 'Text:'
placeholder: Dein Link oder Text...
foreground-color: 'Vordergrundfarbe:'
background-color: 'Hintergrundfarbe:'
error-resistance: 'Fehlerresistenz:'
button:
download: QR-Code herunterladen
wifi-qrcode-generator:
title: WLAN-QR-Code-Generator
description: >-
@ -420,6 +445,11 @@ tools:
description: >-
Informationen zu einem Text erhalten, wie die Anzahl der Zeichen, die
Anzahl der Wörter, die Größe usw.
characters: Anzahl Zeichen
words: Anzahl Wörter
lines: Anzahl Zeilen
bytes: Bytegröße
placeholder: Dein Text...
text-to-nato-alphabet:
title: Text zu NATO-Alphabet
description: >-
@ -430,6 +460,13 @@ tools:
description: >-
Generiere einen Base64-Basic-Auth-Header aus einem Benutzernamen und einem
Passwort.
button:
copy: Header kopieren
copied: Header in die Zwischenablage kopiert
password: Passwort
username: Benutzername
yourpassword: Dein Passwort...
yourusername: Dein Benutzername...
text-to-unicode:
title: Text zu Unicode
description: Parse und konvertiere Text in Unicode und umgekehrt.
@ -454,3 +491,80 @@ tools:
text-to-binary:
title: Text zu ASCII-Binär
description: Konvertiere Text in seine ASCII-Binärrepräsentation und umgekehrt.
safelink-decoder:
title: Outlook Safelink-Decoder
description: Outlook Safelinks decodieren
input: 'Eingabe einer Outlook Safelink-URL:'
input-placeholder: Deine eingegebene Outlook Safelink-URL...
output: 'Ausgabe der decodierten URL:'
ascii-text-drawer:
title: ASCII-Art-Text-Generator
description: ASCII-Art-Text mit vielen Schriftarten und Stilen erstellen.
text: 'Dein Text:'
placeholder: Dein zu zeichnender Text
font: 'Schriftart:'
width: 'Breite:'
loading: Schriftart wird geladen...
error: Die aktuellen Einstellungen führten zu einem Fehler.
output: 'ASCII-Art-Text:'
json-to-xml:
title: JSON zu XML
description: JSON in XML konvertieren
input: Dein JSON-Inhalt
input-placeholder: Füge hier deinen JSON-Inhalt ein...
output: Konvertiertes XML
error: Bereitgestelltes JSON ist ungültig.
xml-to-json:
title: XML zu JSON
description: XML in JSON konvertieren
input: Dein XML-Inhalt
input-placeholder: Füge hier deinen XML-Inhalt ein...
output: Konvertiertes JSON
error: Bereitgestelltes XML ist ungültig.
email-normalizer:
title: E-Mail-Normalisierung
description: >-
Vereinheitlichen von E-Mail-Adressen auf ein Standardformat für einen
einfacheren Vergleich. Nützlich für Deduplizierung und Datenbereinigung.
input: 'Unbearbeitete E-Mails zur Normalisierung:'
input-placeholder: Gib hier deine E-Mails ein (eine pro Zeile)...
output: 'Normalisierte E-Mails:'
output-placeholder: Hier werden normalisierte E-Mails angezeigt...
button:
clear: E-Mails leeren
copy: Kopiere normalisierte E-Mails
copied: Normalisierte E-Mails in die Zwischenablage kopiert
markdown-to-html:
title: Markdown zu HTML
description: Markdown in HTML konvertieren und (als PDF) ausdrucken
markdown: 'Dein zu konvertierender Markdown-Inhalt:'
markdownInput: Dein Markdown-Inhalt...
html: 'HTML-Ausgabe:'
button:
print: Als PDF drucken
regex-memo:
title: Regex-Spickzettel
description: Spickzettel für Javascript Regex/Regulärer Ausdruck
regex-tester:
title: Regex-Tester
description: Teste deine regulären Ausdrücke mit Beispieltext.
regex: Regex
regex-input: 'Regex zum Testen:'
regex-input-placeholder: Eingabe des zu testenden regulären Ausdrucks
link: Siehe Spickzettel für reguläre Ausdrücke
text-input: 'Zu prüfender Text:'
text-input-placeholder: Eingabe des zu prüfenden Texts
matches: Treffer
text-index: Index im Text
value: Wert
captures: Erfassungen
groups: Gruppen
sample: Beispiel für passenden Text
diagram: Regex-Diagramm
global: Globale Suche.
ignoreCase: Suche ohne Berücksichtigung der Groß-/Kleinschreibung.
multiline: Ermöglicht die Übereinstimmung von ^ und $ neben Zeilenumbruchzeichen.
dotAll: Lässt . als Treffer für Zeilenumbruchzeichen zu.
unicode: Unicode; behandelt ein Muster als eine Folge von Unicode-Codepunkten.
unicodeSets: Ein Upgrade zum u-Modus mit mehr Unicode-Funktionen.
no-match: Kein Treffer

View file

@ -57,6 +57,20 @@ toolCard:
new: New
search:
label: Search
placeholder: Type to search a tool or a command...
textareaCopyable:
copy: Copy to clipboard
copied: Copied!
spanCopyable:
copy: Copy to clipboard
copied: Copied!
inputCopyable:
copy: Copy to clipboard
copied: Copied!
formatTransformer:
input: Input
input-placeholder: Input...
output: Output
tools:
categories:
favorite-tools: 'Your favorite tools'
@ -78,7 +92,11 @@ tools:
chronometer:
title: Chronometer
description: Monitor the duration of a thing. Basically a chronometer with simple chronometer features.
button:
start: Start
stop: Stop
reset: Reset
token-generator:
title: Token generator
description: Generate random string with the chars you want, uppercase or lowercase letters, numbers and/or symbols.
@ -252,6 +270,13 @@ tools:
qrcode-generator:
title: QR Code generator
description: Generate and download a QR code for a URL (or just plain text), and customize the background and foreground colors.
error-resistance: 'Error resistance:'
background-color: 'Background color:'
foreground-color: 'Foreground color:'
text: 'Text:'
placeholder: Your link or text...
button:
download: Download QR code
wifi-qrcode-generator:
title: WiFi QR Code generator
@ -360,6 +385,11 @@ tools:
text-statistics:
title: Text statistics
description: Get information about a text, the number of characters, the number of words, its size in bytes, ...
characters: Character count
words: Word count
lines: Line count
bytes: Byte size
placeholder: Your text...
text-to-nato-alphabet:
title: Text to NATO alphabet
@ -368,6 +398,13 @@ tools:
basic-auth-generator:
title: Basic auth generator
description: Generate a base64 basic auth header from a username and password.
button:
copy: Copy header
username: Username
yourusername: Your username...
password: Password
yourpassword: Your password...
copied: Header copied to the clipboard
text-to-unicode:
title: Text to Unicode
@ -392,3 +429,88 @@ tools:
text-to-binary:
title: Text to ASCII binary
description: Convert text to its ASCII binary representation and vice-versa.
safelink-decoder:
title: Outlook Safelink decoder
description: Decode Outlook SafeLink links
input: 'Your input Outlook SafeLink Url:'
input-placeholder: Your input Outlook SafeLink Url...
output: 'Output decoded URL:'
ascii-text-drawer:
title: ASCII Art Text Generator
description: Create ASCII art text with many fonts and styles.
text: 'Your text:'
placeholder: Your text to draw
output: 'Ascii Art text:'
font: 'Font:'
width: 'Width:'
loading: Loading font...
error: Current settings resulted in error.
json-to-xml:
title: JSON to XML
description: Convert JSON to XML
input: Your JSON content
input-placeholder: Paste your JSON content here...
output: Converted XML
error: Provided JSON is not valid.
xml-to-json:
title: XML to JSON
description: Convert XML to JSON
input: Your XML content
input-placeholder: Paste your XML content here...
output: Converted JSON
error: Provided XML is not valid.
email-normalizer:
title: Email normalizer
description: >-
Normalize email addresses to a standard format for easier comparison.
Useful for deduplication and data cleaning.
input: 'Raw emails to normalize:'
output: 'Normalized emails:'
input-placeholder: Put your emails here (one per line)...
output-placeholder: Normalized emails will appear here...
button:
clear: Clear emails
copy: Copy normalized emails
copied: Normalized emails copied to the clipboard
markdown-to-html:
title: Markdown to HTML
description: Convert Markdown to HTML and allow to print (as PDF)
markdown: 'Your Markdown to convert:'
markdownInput: Your Markdown content...
html: 'Output HTML:'
button:
print: Print as PDF
regex-memo:
title: Regex cheatsheet
description: Javascript Regex/Regular Expression cheatsheet
regex-tester:
title: Regex Tester
description: Test your regular expressions with sample text.
regex-input: 'Regex to test:'
regex-input-placeholder: Put the regex to test
link: See Regular Expression Cheatsheet
text-input: 'Text to match:'
text-input-placeholder: Put the text to match
matches: Matches
text-index: Index in text
value: Value
captures: Captures
groups: Groups
sample: Sample matching text
diagram: Regex Diagram
global: Global search
ignoreCase: Case-insensitive search
multiline: Allows ^ and $ to match next to newline characters.
dotAll: Allows . to match newline characters.
unicode: Unicode; treat a pattern as a sequence of Unicode code points.
unicodeSets: An upgrade to the u mode with more Unicode features.
regex: Regex
no-match: No match

View file

@ -2,6 +2,7 @@
import _ from 'lodash';
import type { UseValidationRule } from '@/composable/validation';
import CInputText from '@/ui/c-input-text/c-input-text.vue';
import { translate as t } from '@/plugins/i18n.plugin';
const props = withDefaults(
defineProps<{
@ -16,10 +17,10 @@ const props = withDefaults(
{
transformer: _.identity,
inputValidationRules: () => [],
inputLabel: 'Input',
inputLabel: t('formatTransformer.input'),
inputDefault: '',
inputPlaceholder: 'Input...',
outputLabel: 'Output',
inputPlaceholder: t('formatTransformer.input-placeholder'),
outputLabel: t('formatTransformer.output'),
outputLanguage: '',
},
);

View file

@ -1,13 +1,14 @@
<script setup lang="ts">
import { useVModel } from '@vueuse/core';
import { useCopy } from '@/composable/copy';
import { translate as t } from '@/plugins/i18n.plugin';
const props = defineProps<{ value: string }>();
const emit = defineEmits(['update:value']);
const value = useVModel(props, 'value', emit);
const { copy, isJustCopied } = useCopy({ source: value, createToast: false });
const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : 'Copy to clipboard');
const tooltipText = computed(() => isJustCopied.value ? t('inputCopyable.copied') : t('inputCopyable.copy'));
</script>
<template>

View file

@ -1,13 +1,14 @@
<script setup lang="ts">
import { useCopy } from '@/composable/copy';
import { translate as t } from '@/plugins/i18n.plugin';
const props = withDefaults(defineProps<{ value?: string }>(), { value: '' });
const { value } = toRefs(props);
const initialText = 'Copy to clipboard';
const initialText = t('spanCopyable.copy');
const { copy, isJustCopied } = useCopy({ source: value, createToast: false });
const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : initialText);
const tooltipText = computed(() => isJustCopied.value ? t('spanCopyable.copied') : initialText);
</script>
<template>

View file

@ -9,6 +9,7 @@ import yamlHljs from 'highlight.js/lib/languages/yaml';
import iniHljs from 'highlight.js/lib/languages/ini';
import markdownHljs from 'highlight.js/lib/languages/markdown';
import { useCopy } from '@/composable/copy';
import { translate as t } from '@/plugins/i18n.plugin';
const props = withDefaults(
defineProps<{
@ -22,7 +23,7 @@ const props = withDefaults(
followHeightOf: null,
language: 'txt',
copyPlacement: 'top-right',
copyMessage: 'Copy to clipboard',
copyMessage: t('textareaCopyable.copy'),
},
);
hljs.registerLanguage('sql', sqlHljs);
@ -37,7 +38,7 @@ const { value, language, followHeightOf, copyPlacement, copyMessage } = toRefs(p
const { height } = followHeightOf.value ? useElementSize(followHeightOf) : { height: ref(null) };
const { copy, isJustCopied } = useCopy({ source: value, createToast: false });
const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : copyMessage.value);
const tooltipText = computed(() => isJustCopied.value ? t('textareaCopyable.copied') : copyMessage.value);
</script>
<template>

View file

@ -125,7 +125,7 @@ function activateOption(option: PaletteOption) {
</c-button>
<c-modal v-model:open="isModalOpen" class="palette-modal" shadow-xl important:max-w-650px important:pa-12px @keydown="handleKeydown">
<c-input-text ref="inputRef" v-model:value="searchPrompt" raw-text placeholder="Type to search a tool or a command..." autofocus clearable />
<c-input-text ref="inputRef" v-model:value="searchPrompt" raw-text :placeholder="$t('search.placeholder')" autofocus clearable />
<div v-for="(options, category) in filteredSearchResult" :key="category">
<div ml-3 mt-3 text-sm font-bold text-primary op-60>

View file

@ -8,6 +8,7 @@ const width = useStorage('ascii-text-drawer:width', 80);
const output = ref('');
const errored = ref(false);
const processing = ref(false);
const { t } = useI18n();
figlet.defaults({ fontPath: '//unpkg.com/figlet@1.6.0/fonts/' });
@ -44,8 +45,8 @@ const fonts = ['1Row', '3-D', '3D Diagonal', '3D-ASCII', '3x5', '4Max', '5 Line
<c-card style="max-width: 600px;">
<c-input-text
v-model:value="input"
label="Your text:"
placeholder="Your text to draw"
:label="t('tools.ascii-text-drawer.text')"
:placeholder="t('tools.ascii-text-drawer.placeholder')"
raw-text
multiline
rows="4"
@ -58,14 +59,14 @@ const fonts = ['1Row', '3-D', '3D Diagonal', '3D-ASCII', '3x5', '4Max', '5 Line
<c-select
v-model:value="font"
label-position="top"
label="Font:"
:label="t('tools.ascii-text-drawer.font')"
:options="fonts"
searchable="true"
placeholder="Select font to use"
/>
</n-gi>
<n-gi span="2">
<n-form-item label="Width:" label-placement="top" label-width="100" :show-feedback="false">
<n-form-item :label="t('tools.ascii-text-drawer.width')" label-placement="top" label-width="100" :show-feedback="false">
<n-input-number v-model:value="width" min="0" max="10000" w-full placeholder="Width of the text" />
</n-form-item>
</n-gi>
@ -75,14 +76,14 @@ const fonts = ['1Row', '3-D', '3D Diagonal', '3D-ASCII', '3x5', '4Max', '5 Line
<div v-if="processing" flex items-center justify-center>
<n-spin size="medium" />
<span class="ml-2">Loading font...</span>
<span class="ml-2">{{ t('tools.ascii-text-drawer.loading') }}</span>
</div>
<c-alert v-if="errored" mt-1 text-center type="error">
Current settings resulted in error.
{{ t('tools.ascii-text-drawer.error') }}
</c-alert>
<n-form-item v-if="!processing && !errored" label="Ascii Art text:">
<n-form-item v-if="!processing && !errored" :label="t('tools.ascii-text-drawer.output')">
<TextareaCopyable
:value="output"
mb-1 mt-1

View file

@ -1,10 +1,11 @@
import { Artboard } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'ASCII Art Text Generator',
name: translate('tools.ascii-text-drawer.title'),
path: '/ascii-text-drawer',
description: 'Create ASCII art text with many fonts and styles.',
description: translate('tools.ascii-text-drawer.description'),
keywords: ['ascii', 'asciiart', 'text', 'drawer'],
component: () => import('./ascii-text-drawer.vue'),
icon: Artboard,

View file

@ -5,17 +5,25 @@ import { textToBase64 } from '@/utils/base64';
const username = ref('');
const password = ref('');
const header = computed(() => `Authorization: Basic ${textToBase64(`${username.value}:${password.value}`)}`);
const { t } = useI18n();
const { copy } = useCopy({ source: header, text: 'Header copied to the clipboard' });
const { copy } = useCopy({ source: header, text: t('tools.basic-auth-generator.copied') });
</script>
<template>
<div>
<c-input-text v-model:value="username" label="Username" placeholder="Your username..." clearable raw-text mb-5 />
<c-input-text
v-model:value="username"
:label="t('tools.basic-auth-generator.username')"
:placeholder="t('tools.basic-auth-generator.yourusername')"
clearable
raw-text
mb-5
/>
<c-input-text
v-model:value="password"
label="Password"
placeholder="Your password..."
:label="t('tools.basic-auth-generator.password')"
:placeholder="t('tools.basic-auth-generator.yourpassword')"
clearable
raw-text
mb-2
@ -31,7 +39,7 @@ const { copy } = useCopy({ source: header, text: 'Header copied to the clipboard
</c-card>
<div mt-5 flex justify-center>
<c-button @click="copy()">
Copy header
{{ t('tools.basic-auth-generator.button.copy') }}
</c-button>
</div>
</div>

View file

@ -5,6 +5,7 @@ import { formatMs } from './chronometer.service';
const isRunning = ref(false);
const counter = ref(0);
const { t } = useI18n();
let previousRafDate = Date.now();
const { pause: pauseRaf, resume: resumeRaf } = useRafFn(
@ -37,14 +38,14 @@ function pause() {
</c-card>
<div mt-5 flex justify-center gap-3>
<c-button v-if="!isRunning" type="primary" @click="resume">
Start
{{ t('tools.chronometer.button.start') }}
</c-button>
<c-button v-else type="warning" @click="pause">
Stop
{{ t('tools.chronometer.button.stop') }}
</c-button>
<c-button @click="counter = 0">
Reset
{{ t('tools.chronometer.button.reset') }}
</c-button>
</div>
</div>

View file

@ -3,6 +3,7 @@ import { normalizeEmail } from 'email-normalizer';
import { withDefaultOnError } from '@/utils/defaults';
import { useCopy } from '@/composable/copy';
const { t } = useI18n();
const emails = ref('');
const normalizedEmails = computed(() => {
if (!emails.value) {
@ -17,17 +18,17 @@ const normalizedEmails = computed(() => {
.join('\n');
});
const { copy } = useCopy({ source: normalizedEmails, text: 'Normalized emails copied to the clipboard', createToast: true });
const { copy } = useCopy({ source: normalizedEmails, text: t('tools.email-normalizer.copied'), createToast: true });
</script>
<template>
<div>
<div class="mb-2">
Raw emails to normalize:
{{ t('tools.email-normalizer.input') }}
</div>
<c-input-text
v-model:value="emails"
placeholder="Put your emails here (one per line)..."
:placeholder="t('tools.email-normalizer.input-placeholder')"
rows="3"
multiline
autocomplete="off"
@ -39,11 +40,11 @@ const { copy } = useCopy({ source: normalizedEmails, text: 'Normalized emails co
/>
<div class="mb-2 mt-4">
Normalized emails:
{{ t('tools.email-normalizer.output') }}
</div>
<c-input-text
:value="normalizedEmails"
placeholder="Normalized emails will appear here..."
:placeholder="t('tools.email-normalizer.output-placeholder')"
rows="3"
autocomplete="off"
autocorrect="off"
@ -55,10 +56,10 @@ const { copy } = useCopy({ source: normalizedEmails, text: 'Normalized emails co
/>
<div class="mt-4 flex justify-center gap-2">
<c-button @click="emails = ''">
Clear emails
{{ t('tools.email-normalizer.button.clear') }}
</c-button>
<c-button :disabled="!normalizedEmails" @click="copy()">
Copy normalized emails
{{ t('tools.email-normalizer.button.copy') }}
</c-button>
</div>
</div>

View file

@ -1,10 +1,11 @@
import { Mail } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Email normalizer',
name: translate('tools.email-normalizer.title'),
path: '/email-normalizer',
description: 'Normalize email addresses to a standard format for easier comparison. Useful for deduplication and data cleaning.',
description: translate('tools.email-normalizer.description'),
keywords: ['email', 'normalizer'],
component: () => import('./email-normalizer.vue'),
icon: Mail,

View file

@ -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 XML',
name: translate('tools.json-to-xml.title'),
path: '/json-to-xml',
description: 'Convert JSON to XML',
description: translate('tools.json-to-xml.description'),
keywords: ['json', 'xml'],
component: () => import('./json-to-xml.vue'),
icon: Braces,

View file

@ -4,6 +4,7 @@ import JSON5 from 'json5';
import { withDefaultOnError } from '@/utils/defaults';
import type { UseValidationRule } from '@/composable/validation';
const { t } = useI18n();
const defaultValue = '{"a":{"_attributes":{"x":"1.234","y":"It\'s"}}}';
function transformer(value: string) {
return withDefaultOnError(() => {
@ -14,17 +15,17 @@ function transformer(value: string) {
const rules: UseValidationRule<string>[] = [
{
validator: (v: string) => v === '' || JSON5.parse(v),
message: 'Provided JSON is not valid.',
message: t('tools.json-to-xml.error'),
},
];
</script>
<template>
<format-transformer
input-label="Your JSON content"
:input-label="t('tools.json-to-xml.input')"
:input-default="defaultValue"
input-placeholder="Paste your JSON content here..."
output-label="Converted XML"
:input-placeholder="t('tools.json-to-xml.input-placeholder')"
:output-label="t('tools.json-to-xml.output')"
output-language="xml"
:transformer="transformer"
:input-validation-rules="rules"

View file

@ -1,10 +1,11 @@
import { Markdown } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Markdown to HTML',
name: translate('tools.markdown-to-html.title'),
path: '/markdown-to-html',
description: 'Convert Markdown to Html and allow to print (as PDF)',
description: translate('tools.markdown-to-html.description'),
keywords: ['markdown', 'html', 'converter', 'pdf'],
component: () => import('./markdown-to-html.vue'),
icon: Markdown,

View file

@ -7,6 +7,7 @@ const outputHtml = computed(() => {
const md = markdownit();
return md.render(inputMarkdown.value);
});
const { t } = useI18n();
function printHtml() {
const w = window.open();
@ -23,21 +24,21 @@ function printHtml() {
<c-input-text
v-model:value="inputMarkdown"
multiline raw-text
placeholder="Your Markdown content..."
:placeholder="t('tools.markdown-to-html.markdownInput')"
rows="8"
autofocus
label="Your Markdown to convert:"
:label="t('tools.markdown-to-html.markdown')"
/>
<n-divider />
<n-form-item label="Output HTML:">
<n-form-item :label="t('tools.markdown-to-html.html')">
<TextareaCopyable :value="outputHtml" :word-wrap="true" language="html" />
</n-form-item>
<div flex justify-center>
<n-button @click="printHtml">
Print as PDF
{{ t('tools.markdown-to-html.button.print') }}
</n-button>
</div>
</div>

View file

@ -3,6 +3,7 @@ import type { QRCodeErrorCorrectionLevel } from 'qrcode';
import { useQRCode } from './useQRCode';
import { useDownloadFileFromBase64 } from '@/composable/downloadBase64';
const { t } = useI18n();
const foreground = ref('#000000ff');
const background = ref('#ffffffff');
const errorCorrectionLevel = ref<QRCodeErrorCorrectionLevel>('medium');
@ -32,23 +33,23 @@ const { download } = useDownloadFileFromBase64({ source: qrcode, filename: 'qr-c
label-position="left"
label-width="130px"
label-align="right"
label="Text:"
:label="t('tools.qrcode-generator.text')"
multiline
rows="1"
autosize
placeholder="Your link or text..."
:placeholder="t('tools.qrcode-generator.placeholder')"
mb-6
/>
<n-form label-width="130" label-placement="left">
<n-form-item label="Foreground color:">
<n-form-item :label="t('tools.qrcode-generator.foreground-color')">
<n-color-picker v-model:value="foreground" :modes="['hex']" />
</n-form-item>
<n-form-item label="Background color:">
<n-form-item :label="t('tools.qrcode-generator.background-color')">
<n-color-picker v-model:value="background" :modes="['hex']" />
</n-form-item>
<c-select
v-model:value="errorCorrectionLevel"
label="Error resistance:"
:label="t('tools.qrcode-generator.error-resistance')"
label-position="left"
label-width="130px"
label-align="right"
@ -60,7 +61,7 @@ const { download } = useDownloadFileFromBase64({ source: qrcode, filename: 'qr-c
<div flex flex-col items-center gap-3>
<n-image :src="qrcode" width="200" />
<c-button @click="download">
Download qr-code
{{ t('tools.qrcode-generator.button.download') }}
</c-button>
</div>
</n-gi>

View file

@ -1,10 +1,11 @@
import { BrandJavascript } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Regex cheatsheet',
name: translate('tools.regex-memo.title'),
path: '/regex-memo',
description: 'Javascript Regex/Regular Expression cheatsheet',
description: translate('tools.regex-memo.description'),
keywords: ['regex', 'regular', 'expression', 'javascript', 'memo', 'cheatsheet'],
component: () => import('./regex-memo.vue'),
icon: BrandJavascript,

View file

@ -1,10 +1,11 @@
import { Language } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Regex Tester',
name: translate('tools.regex-tester.title'),
path: '/regex-tester',
description: 'Test your regular expressions with sample text.',
description: translate('tools.regex-tester.description'),
keywords: ['regex', 'tester', 'sample', 'expression'],
component: () => import('./regex-tester.vue'),
icon: Language,

View file

@ -15,6 +15,7 @@ const dotAll = ref(true);
const unicode = ref(true);
const unicodeSets = ref(false);
const visualizerSVG = ref<ShadowRootExpose>();
const { t } = useI18n();
const regexValidation = useValidation({
source: regex,
@ -92,36 +93,36 @@ watchEffect(
<template>
<div max-w-600px>
<c-card title="Regex" mb-1>
<c-card :title="t('tools.regex-tester.regex')" mb-1>
<c-input-text
v-model:value="regex"
label="Regex to test:"
placeholder="Put the regex to test"
:label="t('tools.regex-tester.regex-input')"
:placeholder="t('tools.regex-tester.regex-input-placeholder')"
multiline
rows="3"
:validation="regexValidation"
/>
<router-link target="_blank" to="/regex-memo" mb-1 mt-1>
See Regular Expression Cheatsheet
{{ t('tools.regex-tester.link') }}
</router-link>
<n-space>
<n-checkbox v-model:checked="global">
<span title="Global search">Global search. (<code>g</code>)</span>
<span :title="t('tools.regex-tester.global')">Global search (<code>g</code>)</span>
</n-checkbox>
<n-checkbox v-model:checked="ignoreCase">
<span title="Case-insensitive search">Case-insensitive search. (<code>i</code>)</span>
<span :title="t('tools.regex-tester.ignoreCase')">Case-insensitive search (<code>i</code>)</span>
</n-checkbox>
<n-checkbox v-model:checked="multiline">
<span title="Allows ^ and $ to match next to newline characters.">Multiline(<code>m</code>)</span>
<span :title="t('tools.regex-tester.multiline')">Multiline (<code>m</code>)</span>
</n-checkbox>
<n-checkbox v-model:checked="dotAll">
<span title="Allows . to match newline characters.">Singleline(<code>s</code>)</span>
<span :title="t('tools.regex-tester.dotAll')">Singleline (<code>s</code>)</span>
</n-checkbox>
<n-checkbox v-model:checked="unicode">
<span title="Unicode; treat a pattern as a sequence of Unicode code points.">Unicode(<code>u</code>)</span>
<span :title="t('tools.regex-tester.unicode')">Unicode (<code>u</code>)</span>
</n-checkbox>
<n-checkbox v-model:checked="unicodeSets">
<span title="An upgrade to the u mode with more Unicode features.">Unicode Sets (<code>v</code>)</span>
<span :title="t('tools.regex-tester.unicodeSets')">Unicode Sets (<code>v</code>)</span>
</n-checkbox>
</n-space>
@ -129,28 +130,28 @@ watchEffect(
<c-input-text
v-model:value="text"
label="Text to match:"
placeholder="Put the text to match"
:label="t('tools.regex-tester.text-input')"
:placeholder="t('tools.regex-tester.text-input-placeholder')"
multiline
rows="5"
/>
</c-card>
<c-card title="Matches" mb-1 mt-3>
<c-card :title="t('tools.regex-tester.matches')" mb-1 mt-3>
<n-table v-if="results?.length > 0">
<thead>
<tr>
<th scope="col">
Index in text
{{ t('tools.regex-tester.text-index') }}
</th>
<th scope="col">
Value
{{ t('tools.regex-tester.value') }}
</th>
<th scope="col">
Captures
{{ t('tools.regex-tester.captures') }}
</th>
<th scope="col">
Groups
{{ t('tools.regex-tester.groups') }}
</th>
</tr>
</thead>
@ -176,15 +177,15 @@ watchEffect(
</tbody>
</n-table>
<c-alert v-else>
No match
{{ t('tools.regex-tester.no-match') }}
</c-alert>
</c-card>
<c-card title="Sample matching text" mt-3>
<c-card :title="t('tools.regex-tester.sample')" mt-3>
<pre style="white-space: pre-wrap; word-break: break-all;">{{ sample }}</pre>
</c-card>
<c-card title="Regex Diagram" style="overflow-x: scroll;" mt-3>
<c-card :title="t('tools.regex-tester.diagram')" style="overflow-x: scroll;" mt-3>
<shadow-root ref="visualizerSVG">
&#xa0;
</shadow-root>

View file

@ -1,10 +1,11 @@
import { Mailbox } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Outlook Safelink decoder',
name: translate('tools.safelink-decoder.title'),
path: '/safelink-decoder',
description: 'Decode Outlook SafeLink links',
description: translate('tools.safelink-decoder.description'),
keywords: ['outlook', 'safelink', 'decoder'],
component: () => import('./safelink-decoder.vue'),
icon: Mailbox,

View file

@ -2,6 +2,7 @@
import { decodeSafeLinksURL } from './safelink-decoder.service';
import TextareaCopyable from '@/components/TextareaCopyable.vue';
const { t } = useI18n();
const inputSafeLinkUrl = ref('');
const outputDecodedUrl = computed(() => {
try {
@ -18,14 +19,14 @@ const outputDecodedUrl = computed(() => {
<c-input-text
v-model:value="inputSafeLinkUrl"
raw-text
placeholder="Your input Outlook SafeLink Url..."
:placeholder="t('tools.safelink-decoder.input-placeholder')"
autofocus
label="Your input Outlook SafeLink Url:"
:label="t('tools.safelink-decoder.input')"
/>
<n-divider />
<n-form-item label="Output decoded URL:">
<n-form-item :label="t('tools.safelink-decoder.output')">
<TextareaCopyable :value="outputDecodedUrl" :word-wrap="true" />
</n-form-item>
</div>

View file

@ -3,17 +3,18 @@ import { getStringSizeInBytes } from './text-statistics.service';
import { formatBytes } from '@/utils/convert';
const text = ref('');
const { t } = useI18n();
</script>
<template>
<c-card>
<c-input-text v-model:value="text" multiline placeholder="Your text..." rows="5" />
<c-input-text v-model:value="text" multiline :placeholder="t('tools.text-statistics.placeholder')" rows="5" />
<div mt-5 flex>
<n-statistic label="Character count" :value="text.length" flex-1 />
<n-statistic label="Word count" :value="text === '' ? 0 : text.split(/\s+/).length" flex-1 />
<n-statistic label="Line count" :value="text === '' ? 0 : text.split(/\r\n|\r|\n/).length" flex-1 />
<n-statistic label="Byte size" :value="formatBytes(getStringSizeInBytes(text))" flex-1 />
<n-statistic :label="t('tools.text-statistics.characters')" :value="text.length" flex-1 />
<n-statistic :label="t('tools.text-statistics.words')" :value="text === '' ? 0 : text.split(/\s+/).length" flex-1 />
<n-statistic :label="t('tools.text-statistics.lines')" :value="text === '' ? 0 : text.split(/\r\n|\r|\n/).length" flex-1 />
<n-statistic :label="t('tools.text-statistics.bytes')" :value="formatBytes(getStringSizeInBytes(text))" flex-1 />
</div>
</c-card>
</template>

View file

@ -1,10 +1,11 @@
import { Braces } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'XML to JSON',
name: translate('tools.xml-to-json.title'),
path: '/xml-to-json',
description: 'Convert XML to JSON',
description: translate('tools.xml-to-json.description'),
keywords: ['xml', 'json'],
component: () => import('./xml-to-json.vue'),
icon: Braces,

View file

@ -4,6 +4,7 @@ import { isValidXML } from '../xml-formatter/xml-formatter.service';
import { withDefaultOnError } from '@/utils/defaults';
import type { UseValidationRule } from '@/composable/validation';
const { t } = useI18n();
const defaultValue = '<a x="1.234" y="It\'s"/>';
function transformer(value: string) {
return withDefaultOnError(() => {
@ -14,17 +15,17 @@ function transformer(value: string) {
const rules: UseValidationRule<string>[] = [
{
validator: isValidXML,
message: 'Provided XML is not valid.',
message: t('tools.xml-to-json.error'),
},
];
</script>
<template>
<format-transformer
input-label="Your XML content"
:input-label="t('tools.xml-to-json.input')"
:input-default="defaultValue"
input-placeholder="Paste your XML content here..."
output-label="Converted JSON"
:input-placeholder="t('tools.xml-to-json.input-placeholder')"
:output-label="t('tools.xml-to-json.output')"
output-language="json"
:transformer="transformer"
:input-validation-rules="rules"