mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-05-05 13:57:10 -04:00
WIP(translate): translate two category all tools
This commit is contained in:
parent
9db4b41daf
commit
4f550a9499
58 changed files with 708 additions and 172 deletions
1
components.d.ts
vendored
1
components.d.ts
vendored
|
@ -80,6 +80,7 @@ declare module '@vue/runtime-core' {
|
||||||
FormatTransformer: typeof import('./src/components/FormatTransformer.vue')['default']
|
FormatTransformer: typeof import('./src/components/FormatTransformer.vue')['default']
|
||||||
GitMemo: typeof import('./src/tools/git-memo/git-memo.vue')['default']
|
GitMemo: typeof import('./src/tools/git-memo/git-memo.vue')['default']
|
||||||
'GitMemo.content': typeof import('./src/tools/git-memo/git-memo.content.md')['default']
|
'GitMemo.content': typeof import('./src/tools/git-memo/git-memo.content.md')['default']
|
||||||
|
'GitMemo.content.zh': typeof import('./src/tools/git-memo/git-memo.content.zh.md')['default']
|
||||||
HashText: typeof import('./src/tools/hash-text/hash-text.vue')['default']
|
HashText: typeof import('./src/tools/hash-text/hash-text.vue')['default']
|
||||||
HmacGenerator: typeof import('./src/tools/hmac-generator/hmac-generator.vue')['default']
|
HmacGenerator: typeof import('./src/tools/hmac-generator/hmac-generator.vue')['default']
|
||||||
'Home.page': typeof import('./src/pages/Home.page.vue')['default']
|
'Home.page': typeof import('./src/pages/Home.page.vue')['default']
|
||||||
|
|
|
@ -19,6 +19,8 @@ const {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const video = ref<HTMLVideoElement>();
|
const video = ref<HTMLVideoElement>();
|
||||||
const medias = ref<Media[]>([]);
|
const medias = ref<Media[]>([]);
|
||||||
const currentCamera = ref(cameras.value[0]?.deviceId);
|
const currentCamera = ref(cameras.value[0]?.deviceId);
|
||||||
|
@ -106,20 +108,19 @@ function downloadMedia({ type, value, createdAt }: Media) {
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<c-card v-if="!isSupported">
|
<c-card v-if="!isSupported">
|
||||||
Your browser does not support recording video from camera
|
{{ t('tools.camera-recorder.unSupported') }}
|
||||||
</c-card>
|
</c-card>
|
||||||
|
|
||||||
<c-card v-else-if="!permissionGranted" text-center>
|
<c-card v-else-if="!permissionGranted" text-center>
|
||||||
You need to grant permission to use your camera and microphone
|
{{ t('tools.camera-recorder.needPermissionGranted') }}
|
||||||
|
|
||||||
<c-alert v-if="permissionCannotBePrompted" mt-4 text-left>
|
<c-alert v-if="permissionCannotBePrompted" mt-4 text-left>
|
||||||
Your browser has blocked permission request or does not support it. You need to grant permission manually in
|
{{ t('tools.camera-recorder.permissionCannotBePrompted') }}
|
||||||
your browser settings (usually the lock icon in the address bar).
|
|
||||||
</c-alert>
|
</c-alert>
|
||||||
|
|
||||||
<div v-else mt-4 flex justify-center>
|
<div v-else mt-4 flex justify-center>
|
||||||
<c-button @click="requestPermissions">
|
<c-button @click="requestPermissions">
|
||||||
Grant permission
|
{{ t('tools.camera-recorder.grantPermission') }}
|
||||||
</c-button>
|
</c-button>
|
||||||
</div>
|
</div>
|
||||||
</c-card>
|
</c-card>
|
||||||
|
@ -130,24 +131,24 @@ function downloadMedia({ type, value, createdAt }: Media) {
|
||||||
v-model:value="currentCamera"
|
v-model:value="currentCamera"
|
||||||
label-position="left"
|
label-position="left"
|
||||||
label-width="60px"
|
label-width="60px"
|
||||||
label="Video:"
|
:label="t('tools.camera-recorder.cameraLabel')"
|
||||||
:options="cameras.map(({ deviceId, label }) => ({ value: deviceId, label }))"
|
:options="cameras.map(({ deviceId, label }) => ({ value: deviceId, label }))"
|
||||||
placeholder="Select camera"
|
:placeholder="t('tools.camera-recorder.cameraPlaceholder')"
|
||||||
/>
|
/>
|
||||||
<c-select
|
<c-select
|
||||||
v-if="currentMicrophone && microphones.length > 0"
|
v-if="currentMicrophone && microphones.length > 0"
|
||||||
v-model:value="currentMicrophone"
|
v-model:value="currentMicrophone"
|
||||||
label="Audio:"
|
:label="t('tools.camera-recorder.microphoneLabel')"
|
||||||
label-position="left"
|
label-position="left"
|
||||||
label-width="60px"
|
label-width="60px"
|
||||||
:options="microphones.map(({ deviceId, label }) => ({ value: deviceId, label }))"
|
:options="microphones.map(({ deviceId, label }) => ({ value: deviceId, label }))"
|
||||||
placeholder="Select microphone"
|
:placeholder="t('tools.camera-recorder.microphonePlaceholder')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="!isMediaStreamAvailable" mt-3 flex justify-center>
|
<div v-if="!isMediaStreamAvailable" mt-3 flex justify-center>
|
||||||
<c-button type="primary" @click="start">
|
<c-button type="primary" @click="start">
|
||||||
Start webcam
|
{{ t('tools.camera-recorder.startWebcam') }}
|
||||||
</c-button>
|
</c-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -159,32 +160,32 @@ function downloadMedia({ type, value, createdAt }: Media) {
|
||||||
<div flex items-center justify-between gap-2>
|
<div flex items-center justify-between gap-2>
|
||||||
<c-button :disabled="!isMediaStreamAvailable" @click="takeScreenshot">
|
<c-button :disabled="!isMediaStreamAvailable" @click="takeScreenshot">
|
||||||
<span mr-2> <icon-mdi-camera /></span>
|
<span mr-2> <icon-mdi-camera /></span>
|
||||||
Take screenshot
|
{{ t('tools.camera-recorder.takeScreenshot') }}
|
||||||
</c-button>
|
</c-button>
|
||||||
|
|
||||||
<div v-if="isRecordingSupported" flex justify-center gap-2>
|
<div v-if="isRecordingSupported" flex justify-center gap-2>
|
||||||
<c-button v-if="recordingState === 'stopped'" @click="startRecording">
|
<c-button v-if="recordingState === 'stopped'" @click="startRecording">
|
||||||
<span mr-2> <icon-mdi-video /></span>
|
<span mr-2> <icon-mdi-video /></span>
|
||||||
Start recording
|
{{ t('tools.camera-recorder.startRecording') }}
|
||||||
</c-button>
|
</c-button>
|
||||||
|
|
||||||
<c-button v-if="recordingState === 'recording'" @click="pauseRecording">
|
<c-button v-if="recordingState === 'recording'" @click="pauseRecording">
|
||||||
<span mr-2> <icon-mdi-pause /></span>
|
<span mr-2> <icon-mdi-pause /></span>
|
||||||
Pause
|
{{ t('tools.camera-recorder.pause') }}
|
||||||
</c-button>
|
</c-button>
|
||||||
|
|
||||||
<c-button v-if="recordingState === 'paused'" @click="resumeRecording">
|
<c-button v-if="recordingState === 'paused'" @click="resumeRecording">
|
||||||
<span mr-2> <icon-mdi-play /></span>
|
<span mr-2> <icon-mdi-play /></span>
|
||||||
Resume
|
{{ t('tools.camera-recorder.resume') }}
|
||||||
</c-button>
|
</c-button>
|
||||||
|
|
||||||
<c-button v-if="recordingState !== 'stopped'" type="error" @click="stopRecording">
|
<c-button v-if="recordingState !== 'stopped'" type="error" @click="stopRecording">
|
||||||
<span mr-2> <icon-mdi-record /></span>
|
<span mr-2> <icon-mdi-record /></span>
|
||||||
Stop
|
{{ t('tools.camera-recorder.stop') }}
|
||||||
</c-button>
|
</c-button>
|
||||||
</div>
|
</div>
|
||||||
<div v-else italic op-60>
|
<div v-else italic op-60>
|
||||||
Video recording is not supported in your browser
|
{{ t('tools.camera-recorder.unSupportRecord') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -192,13 +193,13 @@ function downloadMedia({ type, value, createdAt }: Media) {
|
||||||
|
|
||||||
<div grid grid-cols-2 mt-5 gap-2>
|
<div grid grid-cols-2 mt-5 gap-2>
|
||||||
<c-card v-for="({ type, value, createdAt }, index) in medias" :key="index">
|
<c-card v-for="({ type, value, createdAt }, index) in medias" :key="index">
|
||||||
<img v-if="type === 'image'" :src="value" max-h-full w-full alt="screenshot">
|
<img v-if="type === 'image'" :src="value" max-h-full w-full :alt="t('tools.camera-recorder.screenshot')">
|
||||||
|
|
||||||
<video v-else :src="value" controls max-h-full w-full />
|
<video v-else :src="value" controls max-h-full w-full />
|
||||||
|
|
||||||
<div flex items-center justify-between>
|
<div flex items-center justify-between>
|
||||||
<div font-bold>
|
<div font-bold>
|
||||||
{{ type === 'image' ? 'Screenshot' : 'Video' }}
|
{{ type === 'image' ? t('tools.camera-recorder.screenshot') : t('tools.camera-recorder.video') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div flex gap-2>
|
<div flex gap-2>
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { Camera } from '@vicons/tabler';
|
import { Camera } from '@vicons/tabler';
|
||||||
import { defineTool } from '../tool';
|
import { defineTool } from '../tool';
|
||||||
|
import { translate as t } from '@/plugins/i18n.plugin';
|
||||||
|
|
||||||
export const tool = defineTool({
|
export const tool = defineTool({
|
||||||
name: 'Camera recorder',
|
name: t('tools.camera-recorder.title'),
|
||||||
path: '/camera-recorder',
|
path: '/camera-recorder',
|
||||||
description: 'Take a picture or record a video from your webcam or camera.',
|
description: t('tools.camera-recorder.description'),
|
||||||
keywords: ['camera', 'recoder'],
|
keywords: ['camera', 'recoder'],
|
||||||
component: () => import('./camera-recorder.vue'),
|
component: () => import('./camera-recorder.vue'),
|
||||||
icon: Camera,
|
icon: Camera,
|
||||||
|
|
22
src/tools/camera-recorder/locales/en.yml
Normal file
22
src/tools/camera-recorder/locales/en.yml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
tools:
|
||||||
|
camera-recorder:
|
||||||
|
title: Camera recorder
|
||||||
|
description: Take a picture or record a video from your webcam or camera.
|
||||||
|
|
||||||
|
unSupported: Your browser does not support recording video from camera
|
||||||
|
needPermissionGranted: You need to grant permission to use your camera and microphone
|
||||||
|
permissionCannotBePrompted: Your browser has blocked permission request or does not support it. You need to grant permission manually in your browser settings (usually the lock icon in the address bar).
|
||||||
|
grantPermission: Grant permission
|
||||||
|
cameraLabel: 'Video:'
|
||||||
|
cameraPlaceholder: Select camera
|
||||||
|
microphoneLabel: 'Audio:'
|
||||||
|
microphonePlaceholder: Select microphone
|
||||||
|
startWebcam: Start webcam
|
||||||
|
takeScreenshot: Take screenshot
|
||||||
|
startRecording: Start recording
|
||||||
|
pause: Pause
|
||||||
|
resume: Resume
|
||||||
|
stop: Stop
|
||||||
|
unSupportRecord: Video recording is not supported in your browser
|
||||||
|
screenshot: screenshot
|
||||||
|
video: Video
|
22
src/tools/camera-recorder/locales/zh.yml
Normal file
22
src/tools/camera-recorder/locales/zh.yml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
tools:
|
||||||
|
camera-recorder:
|
||||||
|
title: 摄像头录制器
|
||||||
|
description: 从您的网络摄像头或相机拍照或录制视频。
|
||||||
|
|
||||||
|
unSupported: 您的浏览器不支持从摄像头录制视频
|
||||||
|
needPermissionGranted: 您需要授予使用摄像头和麦克风的权限
|
||||||
|
permissionCannotBePrompted: 您的浏览器已阻止权限请求或不支持它。 您需要在浏览器设置中手动授予权限(通常在地址栏中的锁图标)。
|
||||||
|
grantPermission: 授予权限
|
||||||
|
cameraLabel: '视频:'
|
||||||
|
cameraPlaceholder: 选择摄像头
|
||||||
|
microphoneLabel: '音频:'
|
||||||
|
microphonePlaceholder: 选择麦克风
|
||||||
|
startWebcam: 启动网络摄像头
|
||||||
|
takeScreenshot: 拍照
|
||||||
|
startRecording: 开始录制
|
||||||
|
pause: 暂停
|
||||||
|
resume: 恢复
|
||||||
|
stop: 停止
|
||||||
|
unSupportRecord: 您的浏览器不支持视频录制
|
||||||
|
screenshot: 截图
|
||||||
|
video: 视频
|
|
@ -7,11 +7,12 @@ import { computeChmodOctalRepresentation, computeChmodSymbolicRepresentation } f
|
||||||
import type { Group, Scope } from './chmod-calculator.types';
|
import type { Group, Scope } from './chmod-calculator.types';
|
||||||
|
|
||||||
const themeVars = useThemeVars();
|
const themeVars = useThemeVars();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const scopes: { scope: Scope; title: string }[] = [
|
const scopes: { scope: Scope; title: string }[] = [
|
||||||
{ scope: 'read', title: 'Read (4)' },
|
{ scope: 'read', title: t('tools.chmod-calculator.read') },
|
||||||
{ scope: 'write', title: 'Write (2)' },
|
{ scope: 'write', title: t('tools.chmod-calculator.write') },
|
||||||
{ scope: 'execute', title: 'Execute (1)' },
|
{ scope: 'execute', title: t('tools.chmod-calculator.execute') },
|
||||||
];
|
];
|
||||||
const groups: Group[] = ['owner', 'group', 'public'];
|
const groups: Group[] = ['owner', 'group', 'public'];
|
||||||
|
|
||||||
|
@ -32,13 +33,13 @@ const symbolic = computed(() => computeChmodSymbolicRepresentation({ permissions
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-center" scope="col" />
|
<th class="text-center" scope="col" />
|
||||||
<th class="text-center" scope="col">
|
<th class="text-center" scope="col">
|
||||||
Owner (u)
|
{{ t('tools.chmod-calculator.owner') }}
|
||||||
</th>
|
</th>
|
||||||
<th class="text-center" scope="col">
|
<th class="text-center" scope="col">
|
||||||
Group (g)
|
{{ t('tools.chmod-calculator.group') }}
|
||||||
</th>
|
</th>
|
||||||
<th class="text-center" scope="col">
|
<th class="text-center" scope="col">
|
||||||
Public (o)
|
{{ t('tools.chmod-calculator.public') }}
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { FileInvoice } from '@vicons/tabler';
|
import { FileInvoice } from '@vicons/tabler';
|
||||||
import { defineTool } from '../tool';
|
import { defineTool } from '../tool';
|
||||||
|
import { translate as t } from '@/plugins/i18n.plugin';
|
||||||
|
|
||||||
export const tool = defineTool({
|
export const tool = defineTool({
|
||||||
name: 'Chmod calculator',
|
name: t('tools.chmod-calculator.title'),
|
||||||
path: '/chmod-calculator',
|
path: '/chmod-calculator',
|
||||||
description: 'Compute your chmod permissions and commands with this online chmod calculator.',
|
description: t('tools.chmod-calculator.description'),
|
||||||
keywords: [
|
keywords: [
|
||||||
'chmod',
|
'chmod',
|
||||||
'calculator',
|
'calculator',
|
||||||
|
|
11
src/tools/chmod-calculator/locales/en.yml
Normal file
11
src/tools/chmod-calculator/locales/en.yml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
tools:
|
||||||
|
chmod-calculator:
|
||||||
|
title: Chmod calculator
|
||||||
|
description: Compute your chmod permissions and commands with this online chmod calculator.
|
||||||
|
|
||||||
|
read: 'Read (4)'
|
||||||
|
write: 'Write (2)'
|
||||||
|
execute: 'Execute (1)'
|
||||||
|
owner: 'Owner (u)'
|
||||||
|
group: 'Group (g)'
|
||||||
|
public: Public (o)
|
11
src/tools/chmod-calculator/locales/zh.yml
Normal file
11
src/tools/chmod-calculator/locales/zh.yml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
tools:
|
||||||
|
chmod-calculator:
|
||||||
|
title: Chmod 计算器
|
||||||
|
description: 使用此在线 Chmod 计算器计算您的 Chmod 权限和命令。
|
||||||
|
|
||||||
|
read: '读取 (4)'
|
||||||
|
write: '写入 (2)'
|
||||||
|
execute: '执行 (1)'
|
||||||
|
owner: '所有者 (u)'
|
||||||
|
group: '群组 (g)'
|
||||||
|
public: 公共 (o)
|
|
@ -1,5 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import cronstrue from 'cronstrue';
|
import cronstrue from 'cronstrue';
|
||||||
|
import 'cronstrue/locales/zh_CN';
|
||||||
|
import 'cronstrue/locales/fr';
|
||||||
import { isValidCron } from 'cron-validator';
|
import { isValidCron } from 'cron-validator';
|
||||||
import { useStyleStore } from '@/stores/style.store';
|
import { useStyleStore } from '@/stores/style.store';
|
||||||
|
|
||||||
|
@ -9,84 +11,86 @@ function isCronValid(v: string) {
|
||||||
|
|
||||||
const styleStore = useStyleStore();
|
const styleStore = useStyleStore();
|
||||||
|
|
||||||
|
const { t, locale } = useI18n();
|
||||||
const cron = ref('40 * * * *');
|
const cron = ref('40 * * * *');
|
||||||
const cronstrueConfig = reactive({
|
const cronstrueConfig = reactive({
|
||||||
verbose: true,
|
verbose: true,
|
||||||
dayOfWeekStartIndexZero: true,
|
dayOfWeekStartIndexZero: true,
|
||||||
use24HourTimeFormat: true,
|
use24HourTimeFormat: true,
|
||||||
throwExceptionOnParseError: true,
|
throwExceptionOnParseError: true,
|
||||||
|
locale: locale.value === 'zh' ? 'zh_CN' : locale,
|
||||||
});
|
});
|
||||||
|
|
||||||
const helpers = [
|
const helpers = [
|
||||||
{
|
{
|
||||||
symbol: '*',
|
symbol: '*',
|
||||||
meaning: 'Any value',
|
meaning: t('tools.crontab-generator.anyMeaning'),
|
||||||
example: '* * * *',
|
example: '* * * *',
|
||||||
equivalent: 'Every minute',
|
equivalent: t('tools.crontab-generator.anyExample'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
symbol: '-',
|
symbol: '-',
|
||||||
meaning: 'Range of values',
|
meaning: t('tools.crontab-generator.rangeMeaning'),
|
||||||
example: '1-10 * * *',
|
example: '1-10 * * *',
|
||||||
equivalent: 'Minutes 1 through 10',
|
equivalent: t('tools.crontab-generator.rangeExample'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
symbol: ',',
|
symbol: ',',
|
||||||
meaning: 'List of values',
|
meaning: t('tools.crontab-generator.listMeaning'),
|
||||||
example: '1,10 * * *',
|
example: '1,10 * * *',
|
||||||
equivalent: 'At minutes 1 and 10',
|
equivalent: t('tools.crontab-generator.listExample'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
symbol: '/',
|
symbol: '/',
|
||||||
meaning: 'Step values',
|
meaning: t('tools.crontab-generator.stepMeaning'),
|
||||||
example: '*/10 * * *',
|
example: '*/10 * * *',
|
||||||
equivalent: 'Every 10 minutes',
|
equivalent: t('tools.crontab-generator.stepExample'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
symbol: '@yearly',
|
symbol: '@yearly',
|
||||||
meaning: 'Once every year at midnight of 1 January',
|
meaning: t('tools.crontab-generator.yearlyMeaning'),
|
||||||
example: '@yearly',
|
example: '@yearly',
|
||||||
equivalent: '0 0 1 1 *',
|
equivalent: '0 0 1 1 *',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
symbol: '@annually',
|
symbol: '@annually',
|
||||||
meaning: 'Same as @yearly',
|
meaning: t('tools.crontab-generator.annuallyMeaning', { yearly: '@yearly' }),
|
||||||
example: '@annually',
|
example: '@annually',
|
||||||
equivalent: '0 0 1 1 *',
|
equivalent: '0 0 1 1 *',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
symbol: '@monthly',
|
symbol: '@monthly',
|
||||||
meaning: 'Once a month at midnight on the first day',
|
meaning: t('tools.crontab-generator.monthlyMeaning'),
|
||||||
example: '@monthly',
|
example: '@monthly',
|
||||||
equivalent: '0 0 1 * *',
|
equivalent: '0 0 1 * *',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
symbol: '@weekly',
|
symbol: '@weekly',
|
||||||
meaning: 'Once a week at midnight on Sunday morning',
|
meaning: t('tools.crontab-generator.weeklyMeaning'),
|
||||||
example: '@weekly',
|
example: '@weekly',
|
||||||
equivalent: '0 0 * * 0',
|
equivalent: '0 0 * * 0',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
symbol: '@daily',
|
symbol: '@daily',
|
||||||
meaning: 'Once a day at midnight',
|
meaning: t('tools.crontab-generator.dailyMeaning'),
|
||||||
example: '@daily',
|
example: '@daily',
|
||||||
equivalent: '0 0 * * *',
|
equivalent: '0 0 * * *',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
symbol: '@midnight',
|
symbol: '@midnight',
|
||||||
meaning: 'Same as @daily',
|
meaning: t('tools.crontab-generator.midnightMeaning', { daily: '@daily' }),
|
||||||
example: '@midnight',
|
example: '@midnight',
|
||||||
equivalent: '0 0 * * *',
|
equivalent: '0 0 * * *',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
symbol: '@hourly',
|
symbol: '@hourly',
|
||||||
meaning: 'Once an hour at the beginning of the hour',
|
meaning: t('tools.crontab-generator.hourlyMeaning'),
|
||||||
example: '@hourly',
|
example: '@hourly',
|
||||||
equivalent: '0 * * * *',
|
equivalent: '0 * * * *',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
symbol: '@reboot',
|
symbol: '@reboot',
|
||||||
meaning: 'Run at startup',
|
meaning: t('tools.crontab-generator.rebootMeaning'),
|
||||||
example: '',
|
example: '',
|
||||||
equivalent: '',
|
equivalent: '',
|
||||||
},
|
},
|
||||||
|
@ -102,7 +106,7 @@ const cronString = computed(() => {
|
||||||
const cronValidationRules = [
|
const cronValidationRules = [
|
||||||
{
|
{
|
||||||
validator: (value: string) => isCronValid(value),
|
validator: (value: string) => isCronValid(value),
|
||||||
message: 'This cron is invalid',
|
message: t('tools.crontab-generator.invalidMessage'),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
|
@ -127,13 +131,13 @@ const cronValidationRules = [
|
||||||
|
|
||||||
<div flex justify-center>
|
<div flex justify-center>
|
||||||
<n-form :show-feedback="false" label-width="170" label-placement="left">
|
<n-form :show-feedback="false" label-width="170" label-placement="left">
|
||||||
<n-form-item label="Verbose">
|
<n-form-item :label="t('tools.crontab-generator.verbose')">
|
||||||
<n-switch v-model:value="cronstrueConfig.verbose" />
|
<n-switch v-model:value="cronstrueConfig.verbose" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="Use 24 hour time format">
|
<n-form-item :label="t('tools.crontab-generator.use24HourTimeFormat')">
|
||||||
<n-switch v-model:value="cronstrueConfig.use24HourTimeFormat" />
|
<n-switch v-model:value="cronstrueConfig.use24HourTimeFormat" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="Days start at 0">
|
<n-form-item :label="t('tools.crontab-generator.dayOfWeekStartIndexZero')">
|
||||||
<n-switch v-model:value="cronstrueConfig.dayOfWeekStartIndexZero" />
|
<n-switch v-model:value="cronstrueConfig.dayOfWeekStartIndexZero" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-form>
|
</n-form>
|
||||||
|
@ -141,29 +145,29 @@ const cronValidationRules = [
|
||||||
</c-card>
|
</c-card>
|
||||||
<c-card>
|
<c-card>
|
||||||
<pre>
|
<pre>
|
||||||
┌──────────── [optional] seconds (0 - 59)
|
┌──────────── {{ t('tools.crontab-generator.secondDesc') }}
|
||||||
| ┌────────── minute (0 - 59)
|
| ┌────────── {{ t('tools.crontab-generator.minuteDesc') }}
|
||||||
| | ┌──────── hour (0 - 23)
|
| | ┌──────── {{ t('tools.crontab-generator.hourDesc') }}
|
||||||
| | | ┌────── day of month (1 - 31)
|
| | | ┌────── {{ t('tools.crontab-generator.dayOfMonthDesc') }}
|
||||||
| | | | ┌──── month (1 - 12) OR jan,feb,mar,apr ...
|
| | | | ┌──── {{ t('tools.crontab-generator.monthDesc') }}
|
||||||
| | | | | ┌── day of week (0 - 6, sunday=0) OR sun,mon ...
|
| | | | | ┌── {{ t('tools.crontab-generator.dayOfWeekDesc') }}
|
||||||
| | | | | |
|
| | | | | |
|
||||||
* * * * * * command</pre>
|
* * * * * * {{ t('tools.crontab-generator.command') }}</pre>
|
||||||
|
|
||||||
<div v-if="styleStore.isSmallScreen">
|
<div v-if="styleStore.isSmallScreen">
|
||||||
<c-card v-for="{ symbol, meaning, example, equivalent } in helpers" :key="symbol" mb-3 important:border-none>
|
<c-card v-for="{ symbol, meaning, example, equivalent } in helpers" :key="symbol" mb-3 important:border-none>
|
||||||
<div>
|
<div>
|
||||||
Symbol: <strong>{{ symbol }}</strong>
|
{{ t('tools.crontab-generator.symbol') }} <strong>{{ symbol }}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
Meaning: <strong>{{ meaning }}</strong>
|
{{ t('tools.crontab-generator.meaning') }} <strong>{{ meaning }}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
Example:
|
{{ t('tools.crontab-generator.example') }}
|
||||||
<strong><code>{{ example }}</code></strong>
|
<strong><code>{{ example }}</code></strong>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
Equivalent: <strong>{{ equivalent }}</strong>
|
{{ t('tools.crontab-generator.equivalent') }} <strong>{{ equivalent }}</strong>
|
||||||
</div>
|
</div>
|
||||||
</c-card>
|
</c-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { Alarm } from '@vicons/tabler';
|
import { Alarm } from '@vicons/tabler';
|
||||||
import { defineTool } from '../tool';
|
import { defineTool } from '../tool';
|
||||||
|
import { translate as t } from '@/plugins/i18n.plugin';
|
||||||
|
|
||||||
export const tool = defineTool({
|
export const tool = defineTool({
|
||||||
name: 'Crontab generator',
|
name: t('tools.crontab-generator.title'),
|
||||||
path: '/crontab-generator',
|
path: '/crontab-generator',
|
||||||
description: 'Validate and generate crontab and get the human readable description of the cron schedule.',
|
description: t('tools.crontab-generator.description'),
|
||||||
keywords: [
|
keywords: [
|
||||||
'crontab',
|
'crontab',
|
||||||
'generator',
|
'generator',
|
||||||
|
|
38
src/tools/crontab-generator/locales/en.yml
Normal file
38
src/tools/crontab-generator/locales/en.yml
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
tools:
|
||||||
|
crontab-generator:
|
||||||
|
title: Crontab generator
|
||||||
|
description: Validate and generate crontab and get the human readable description of the cron schedule.
|
||||||
|
|
||||||
|
anyMeaning: Any value
|
||||||
|
anyExample: Every minute
|
||||||
|
rangeMeaning: Range of values
|
||||||
|
rangeExample: Minutes 1 through 10
|
||||||
|
listMeaning: List of values
|
||||||
|
listExample: At minutes 1 and 10
|
||||||
|
stepMeaning: Step values
|
||||||
|
stepExample: Every 10 minutes
|
||||||
|
yearlyMeaning: Once every year at midnight of 1 January
|
||||||
|
annuallyMeaning: Same as {yearly}
|
||||||
|
monthlyMeaning: Once a month at midnight on the first day
|
||||||
|
weeklyMeaning: Once a week at midnight on Sunday morning
|
||||||
|
dailyMeaning: Once a day at midnight
|
||||||
|
midnightMeaning: Same as {daily}
|
||||||
|
hourlyMeaning: Once an hour at the beginning of the hour
|
||||||
|
rebootMeaning: Run at startup
|
||||||
|
|
||||||
|
verbose: Verbose
|
||||||
|
use24HourTimeFormat: Use 24 hour time format
|
||||||
|
dayOfWeekStartIndexZero: Days start at 0
|
||||||
|
secondDesc: '[optional] seconds (0 - 59)'
|
||||||
|
minuteDesc: 'minute (0 - 59)'
|
||||||
|
hourDesc: 'hour (0 - 23)'
|
||||||
|
dayOfMonthDesc: 'day of month (1 - 31)'
|
||||||
|
monthDesc: 'month (1 - 12) OR jan,feb,mar,apr ...'
|
||||||
|
dayOfWeekDesc: 'day of week (0 - 6, sunday=0) OR sun,mon ...'
|
||||||
|
command: command
|
||||||
|
symbol: 'Symbol:'
|
||||||
|
meaning: 'Meaning:'
|
||||||
|
example: 'Example:'
|
||||||
|
equivalent: 'Equivalent:'
|
||||||
|
|
||||||
|
invalidMessage: This cron is invalid
|
38
src/tools/crontab-generator/locales/zh.yml
Normal file
38
src/tools/crontab-generator/locales/zh.yml
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
tools:
|
||||||
|
crontab-generator:
|
||||||
|
title: 定时任务生成器
|
||||||
|
description: 验证和生成 crontab,并获取定时任务的可读描述。
|
||||||
|
|
||||||
|
anyMeaning: 任意值
|
||||||
|
anyExample: 每分钟
|
||||||
|
rangeMeaning: 值范围
|
||||||
|
rangeExample: 1 到 10 分钟
|
||||||
|
listMeaning: 值列表
|
||||||
|
listExample: 在第 1 和第 10 分钟
|
||||||
|
stepMeaning: 步进值
|
||||||
|
stepExample: 每 10 分钟
|
||||||
|
yearlyMeaning: 每年 1 月 1 日午夜
|
||||||
|
annuallyMeaning: '等同于 {yearly}'
|
||||||
|
monthlyMeaning: 每月第一天午夜
|
||||||
|
weeklyMeaning: 每周星期日凌晨
|
||||||
|
dailyMeaning: 每天午夜
|
||||||
|
midnightMeaning: '等同于 {daily}'
|
||||||
|
hourlyMeaning: 每小时整点
|
||||||
|
rebootMeaning: 启动时运行
|
||||||
|
|
||||||
|
verbose: 冗长模式
|
||||||
|
use24HourTimeFormat: 使用 24 小时时间格式
|
||||||
|
dayOfWeekStartIndexZero: 一周从 0 开始
|
||||||
|
secondDesc: '[可选] 秒(0 - 59)'
|
||||||
|
minuteDesc: '分钟(0 - 59)'
|
||||||
|
hourDesc: '小时(0 - 23)'
|
||||||
|
dayOfMonthDesc: '日期(1 - 31)'
|
||||||
|
monthDesc: '月份(1 - 12)或 jan、feb、mar、apr ...'
|
||||||
|
dayOfWeekDesc: '星期几(0 - 6,星期日=0)或 sun、mon ...'
|
||||||
|
command: 命令
|
||||||
|
symbol: '符号:'
|
||||||
|
meaning: '含义:'
|
||||||
|
example: '示例:'
|
||||||
|
equivalent: '等同:'
|
||||||
|
|
||||||
|
invalidMessage: 此 cron 表达式无效
|
|
@ -5,6 +5,7 @@ import { useDownloadFileFromBase64 } from '@/composable/downloadBase64';
|
||||||
import { textToBase64 } from '@/utils/base64';
|
import { textToBase64 } from '@/utils/base64';
|
||||||
import TextareaCopyable from '@/components/TextareaCopyable.vue';
|
import TextareaCopyable from '@/components/TextareaCopyable.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
const dockerRun = ref(
|
const dockerRun = ref(
|
||||||
'docker run -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock:ro --restart always --log-opt max-size=1g nginx',
|
'docker run -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock:ro --restart always --log-opt max-size=1g nginx',
|
||||||
);
|
);
|
||||||
|
@ -32,12 +33,12 @@ const { download } = useDownloadFileFromBase64({ source: dockerComposeBase64, fi
|
||||||
<div>
|
<div>
|
||||||
<c-input-text
|
<c-input-text
|
||||||
v-model:value="dockerRun"
|
v-model:value="dockerRun"
|
||||||
label="Your docker run command:"
|
:label="t('tools.docker-run-to-docker-compose-converter.inputLabel')"
|
||||||
style="font-family: monospace"
|
style="font-family: monospace"
|
||||||
multiline
|
multiline
|
||||||
raw-text
|
raw-text
|
||||||
monospace
|
monospace
|
||||||
placeholder="Your docker run command to convert..."
|
:placeholder="t('tools.docker-run-to-docker-compose-converter.inputPlaceholder')"
|
||||||
rows="3"
|
rows="3"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -47,12 +48,12 @@ const { download } = useDownloadFileFromBase64({ source: dockerComposeBase64, fi
|
||||||
|
|
||||||
<div mt-5 flex justify-center>
|
<div mt-5 flex justify-center>
|
||||||
<c-button :disabled="dockerCompose === ''" secondary @click="download">
|
<c-button :disabled="dockerCompose === ''" secondary @click="download">
|
||||||
Download docker-compose.yml
|
{{ t('tools.docker-run-to-docker-compose-converter.downloadBtn') }}
|
||||||
</c-button>
|
</c-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="notComposable.length > 0">
|
<div v-if="notComposable.length > 0">
|
||||||
<n-alert title="This options are not translatable to docker-compose" type="info" mt-5>
|
<n-alert :title="t('tools.docker-run-to-docker-compose-converter.notComposable')" type="info" mt-5>
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="(message, index) of notComposable" :key="index">
|
<li v-for="(message, index) of notComposable" :key="index">
|
||||||
{{ message }}
|
{{ message }}
|
||||||
|
@ -63,7 +64,7 @@ const { download } = useDownloadFileFromBase64({ source: dockerComposeBase64, fi
|
||||||
|
|
||||||
<div v-if="notImplemented.length > 0">
|
<div v-if="notImplemented.length > 0">
|
||||||
<n-alert
|
<n-alert
|
||||||
title="This options are not yet implemented and therefore haven't been translated to docker-compose"
|
:title="t('tools.docker-run-to-docker-compose-converter.notImplemented')"
|
||||||
type="warning"
|
type="warning"
|
||||||
mt-5
|
mt-5
|
||||||
>
|
>
|
||||||
|
@ -76,7 +77,7 @@ const { download } = useDownloadFileFromBase64({ source: dockerComposeBase64, fi
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="errors.length > 0">
|
<div v-if="errors.length > 0">
|
||||||
<n-alert title="The following errors occured" type="error" mt-5>
|
<n-alert :title="t('tools.docker-run-to-docker-compose-converter.errors')" type="error" mt-5>
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="(message, index) of errors" :key="index">
|
<li v-for="(message, index) of errors" :key="index">
|
||||||
{{ message }}
|
{{ message }}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { BrandDocker } from '@vicons/tabler';
|
import { BrandDocker } from '@vicons/tabler';
|
||||||
import { defineTool } from '../tool';
|
import { defineTool } from '../tool';
|
||||||
|
import { translate as t } from '@/plugins/i18n.plugin';
|
||||||
|
|
||||||
export const tool = defineTool({
|
export const tool = defineTool({
|
||||||
name: 'Docker run to Docker compose converter',
|
name: t('tools.docker-run-to-docker-compose-converter.title'),
|
||||||
path: '/docker-run-to-docker-compose-converter',
|
path: '/docker-run-to-docker-compose-converter',
|
||||||
description: 'Turns docker run commands into docker-compose files!',
|
description: t('tools.docker-run-to-docker-compose-converter.description'),
|
||||||
keywords: ['docker', 'run', 'compose', 'yaml', 'yml', 'convert', 'deamon'],
|
keywords: ['docker', 'run', 'compose', 'yaml', 'yml', 'convert', 'deamon'],
|
||||||
component: () => import('./docker-run-to-docker-compose-converter.vue'),
|
component: () => import('./docker-run-to-docker-compose-converter.vue'),
|
||||||
icon: BrandDocker,
|
icon: BrandDocker,
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
tools:
|
||||||
|
docker-run-to-docker-compose-converter:
|
||||||
|
title: Docker run to Docker compose converter
|
||||||
|
description: Turns docker run commands into docker-compose files!
|
||||||
|
|
||||||
|
inputLabel: 'Your docker run command:'
|
||||||
|
inputPlaceholder: Your docker run command to convert...
|
||||||
|
downloadBtn: Download docker-compose.yml
|
||||||
|
|
||||||
|
notComposable: This options are not translatable to docker-compose
|
||||||
|
notImplemented: This options are not yet implemented and therefore haven't been translated to docker-compose
|
||||||
|
errors: The following errors occured
|
|
@ -0,0 +1,12 @@
|
||||||
|
tools:
|
||||||
|
docker-run-to-docker-compose-converter:
|
||||||
|
title: Docker run 转换为 Docker compose 转换器
|
||||||
|
description: 将 docker run 命令转换为 docker-compose 文件!
|
||||||
|
|
||||||
|
inputLabel: '您的 docker run 命令:'
|
||||||
|
inputPlaceholder: 您要转换的 docker run 命令...
|
||||||
|
downloadBtn: 下载 docker-compose.yml
|
||||||
|
|
||||||
|
notComposable: 这些选项无法转换为 docker-compose
|
||||||
|
notImplemented: 这些选项尚未实现,因此尚未被翻译为 docker-compose
|
||||||
|
errors: 发生了以下错误
|
77
src/tools/git-memo/git-memo.content.zh.md
Normal file
77
src/tools/git-memo/git-memo.content.zh.md
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
## 配置
|
||||||
|
|
||||||
|
设置全局配置
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git config --global user.name "[姓名]"
|
||||||
|
git config --global user.email "[邮箱]"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 入门
|
||||||
|
|
||||||
|
创建一个 git 仓库
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git init
|
||||||
|
```
|
||||||
|
|
||||||
|
克隆现有的 git 仓库
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git clone [url]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 提交
|
||||||
|
|
||||||
|
提交所有已跟踪的更改
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git commit -am "[提交信息]"
|
||||||
|
```
|
||||||
|
|
||||||
|
将新修改添加到上次提交中
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git commit --amend --no-edit
|
||||||
|
```
|
||||||
|
|
||||||
|
## 我犯了一个错误
|
||||||
|
|
||||||
|
更改上次提交的消息
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git commit --amend
|
||||||
|
```
|
||||||
|
|
||||||
|
撤消最近的提交并保留更改
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git reset HEAD~1
|
||||||
|
```
|
||||||
|
|
||||||
|
撤消最近的 `N` 个提交并保留更改
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git reset HEAD~N
|
||||||
|
```
|
||||||
|
|
||||||
|
撤消最近的提交并放弃更改
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git reset HEAD~1 --hard
|
||||||
|
```
|
||||||
|
|
||||||
|
将分支重置为远程状态
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git fetch origin
|
||||||
|
git reset --hard origin/[分支名]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 其他
|
||||||
|
|
||||||
|
将本地 master 分支重命名为 main
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git branch -m master main
|
||||||
|
```
|
|
@ -1,13 +1,16 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useThemeVars } from 'naive-ui';
|
import { useThemeVars } from 'naive-ui';
|
||||||
import Memo from './git-memo.content.md';
|
import Memo from './git-memo.content.md';
|
||||||
|
import MemoZH from './git-memo.content.zh.md';
|
||||||
|
|
||||||
const themeVars = useThemeVars();
|
const themeVars = useThemeVars();
|
||||||
|
const { locale } = useI18n();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<Memo />
|
<MemoZH v-if="locale === 'zh'" />
|
||||||
|
<Memo v-else />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { BrandGit } from '@vicons/tabler';
|
import { BrandGit } from '@vicons/tabler';
|
||||||
import { defineTool } from '../tool';
|
import { defineTool } from '../tool';
|
||||||
|
import { translate as t } from '@/plugins/i18n.plugin';
|
||||||
|
|
||||||
export const tool = defineTool({
|
export const tool = defineTool({
|
||||||
name: 'Git cheatsheet',
|
name: t('tools.git-memo.title'),
|
||||||
path: '/git-memo',
|
path: '/git-memo',
|
||||||
description:
|
description: t('tools.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.',
|
|
||||||
keywords: ['git', 'push', 'force', 'pull', 'commit', 'amend', 'rebase', 'merge', 'reset', 'soft', 'hard', 'lease'],
|
keywords: ['git', 'push', 'force', 'pull', 'commit', 'amend', 'rebase', 'merge', 'reset', 'soft', 'hard', 'lease'],
|
||||||
component: () => import('./git-memo.vue'),
|
component: () => import('./git-memo.vue'),
|
||||||
icon: BrandGit,
|
icon: BrandGit,
|
||||||
|
|
4
src/tools/git-memo/locales/en.yml
Normal file
4
src/tools/git-memo/locales/en.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
tools:
|
||||||
|
git-memo:
|
||||||
|
title: Git cheatsheet
|
||||||
|
description: Git is a decentralized version management software. With this cheatsheet you will have a quick access to the most common git commands.
|
4
src/tools/git-memo/locales/zh.yml
Normal file
4
src/tools/git-memo/locales/zh.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
tools:
|
||||||
|
git-memo:
|
||||||
|
title: Git 备忘录
|
||||||
|
description: Git 是一款分布式版本管理软件。使用这个备忘录,您将快速访问最常用的 Git 命令。
|
|
@ -1,10 +1,11 @@
|
||||||
import { Braces } from '@vicons/tabler';
|
import { Braces } from '@vicons/tabler';
|
||||||
import { defineTool } from '../tool';
|
import { defineTool } from '../tool';
|
||||||
|
import { translate as t } from '@/plugins/i18n.plugin';
|
||||||
|
|
||||||
export const tool = defineTool({
|
export const tool = defineTool({
|
||||||
name: 'JSON minify',
|
name: t('tools.json-minify.title'),
|
||||||
path: '/json-minify',
|
path: '/json-minify',
|
||||||
description: 'Minify and compress your JSON by removing unnecessary white spaces.',
|
description: t('tools.json-minify.description'),
|
||||||
keywords: ['json', 'minify', 'format'],
|
keywords: ['json', 'minify', 'format'],
|
||||||
component: () => import('./json-minify.vue'),
|
component: () => import('./json-minify.vue'),
|
||||||
icon: Braces,
|
icon: Braces,
|
||||||
|
|
|
@ -3,23 +3,24 @@ import JSON5 from 'json5';
|
||||||
import type { UseValidationRule } from '@/composable/validation';
|
import type { UseValidationRule } from '@/composable/validation';
|
||||||
import { withDefaultOnError } from '@/utils/defaults';
|
import { withDefaultOnError } from '@/utils/defaults';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
const defaultValue = '{\n\t"hello": [\n\t\t"world"\n\t]\n}';
|
const defaultValue = '{\n\t"hello": [\n\t\t"world"\n\t]\n}';
|
||||||
const transformer = (value: string) => withDefaultOnError(() => JSON.stringify(JSON5.parse(value), null, 0), '');
|
const transformer = (value: string) => withDefaultOnError(() => JSON.stringify(JSON5.parse(value), null, 0), '');
|
||||||
|
|
||||||
const rules: UseValidationRule<string>[] = [
|
const rules: UseValidationRule<string>[] = [
|
||||||
{
|
{
|
||||||
validator: (v: string) => v === '' || JSON5.parse(v),
|
validator: (v: string) => v === '' || JSON5.parse(v),
|
||||||
message: 'Provided JSON is not valid.',
|
message: t('tools.json-minify.invalidMessage'),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<format-transformer
|
<format-transformer
|
||||||
input-label="Your raw JSON"
|
:input-label="t('tools.json-minify.inputLabel')"
|
||||||
:input-default="defaultValue"
|
:input-default="defaultValue"
|
||||||
input-placeholder="Paste your raw JSON here..."
|
:input-placeholder="t('tools.json-minify.inputPlaceholder')"
|
||||||
output-label="Minified version of your JSON"
|
:output-label="t('tools.json-minify.outputLabel')"
|
||||||
output-language="json"
|
output-language="json"
|
||||||
:input-validation-rules="rules"
|
:input-validation-rules="rules"
|
||||||
:transformer="transformer"
|
:transformer="transformer"
|
||||||
|
|
10
src/tools/json-minify/locales/en.yml
Normal file
10
src/tools/json-minify/locales/en.yml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
tools:
|
||||||
|
json-minify:
|
||||||
|
title: JSON minify
|
||||||
|
description: Minify and compress your JSON by removing unnecessary white spaces.
|
||||||
|
|
||||||
|
inputLabel: Your raw JSON
|
||||||
|
inputPlaceholder: Paste your raw JSON here...
|
||||||
|
outputLabel: Minified version of your JSON
|
||||||
|
|
||||||
|
invalidMessage: Provided JSON is not valid.
|
10
src/tools/json-minify/locales/zh.yml
Normal file
10
src/tools/json-minify/locales/zh.yml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
tools:
|
||||||
|
json-minify:
|
||||||
|
title: JSON 压缩
|
||||||
|
description: 通过删除不必要的空格来压缩和压缩您的 JSON。
|
||||||
|
|
||||||
|
inputLabel: 您的原始 JSON
|
||||||
|
inputPlaceholder: 在此粘贴您的原始 JSON...
|
||||||
|
outputLabel: 您的 JSON 的压缩版本
|
||||||
|
|
||||||
|
invalidMessage: 提供的 JSON 不是有效的。
|
|
@ -1,10 +1,11 @@
|
||||||
import { List } from '@vicons/tabler';
|
import { List } from '@vicons/tabler';
|
||||||
import { defineTool } from '../tool';
|
import { defineTool } from '../tool';
|
||||||
|
import { translate as t } from '@/plugins/i18n.plugin';
|
||||||
|
|
||||||
export const tool = defineTool({
|
export const tool = defineTool({
|
||||||
name: 'JSON to CSV',
|
name: t('tools.json-to-csv.title'),
|
||||||
path: '/json-to-csv',
|
path: '/json-to-csv',
|
||||||
description: 'Convert JSON to CSV with automatic header detection.',
|
description: t('tools.json-to-csv.description'),
|
||||||
keywords: ['json', 'to', 'csv', 'convert'],
|
keywords: ['json', 'to', 'csv', 'convert'],
|
||||||
component: () => import('./json-to-csv.vue'),
|
component: () => import('./json-to-csv.vue'),
|
||||||
icon: List,
|
icon: List,
|
||||||
|
|
|
@ -13,19 +13,20 @@ function transformer(value: string) {
|
||||||
}, '');
|
}, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
const rules: UseValidationRule<string>[] = [
|
const rules: UseValidationRule<string>[] = [
|
||||||
{
|
{
|
||||||
validator: (v: string) => v === '' || JSON5.parse(v),
|
validator: (v: string) => v === '' || JSON5.parse(v),
|
||||||
message: 'Provided JSON is not valid.',
|
message: t('tools.json-to-csv.invalidMessage'),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<format-transformer
|
<format-transformer
|
||||||
input-label="Your raw JSON"
|
:input-label="t('tools.json-to-csv.inputLabel')"
|
||||||
input-placeholder="Paste your raw JSON here..."
|
:input-placeholder="t('tools.json-to-csv.inputPlaceholder')"
|
||||||
output-label="CSV version of your JSON"
|
:output-label="t('tools.json-to-csv.outputLabel')"
|
||||||
:input-validation-rules="rules"
|
:input-validation-rules="rules"
|
||||||
:transformer="transformer"
|
:transformer="transformer"
|
||||||
/>
|
/>
|
||||||
|
|
10
src/tools/json-to-csv/locales/en.yml
Normal file
10
src/tools/json-to-csv/locales/en.yml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
tools:
|
||||||
|
json-to-csv:
|
||||||
|
title: JSON to CSV
|
||||||
|
description: Convert JSON to CSV with automatic header detection.
|
||||||
|
|
||||||
|
inputLabel: Your raw JSON
|
||||||
|
inputPlaceholder: Paste your raw JSON here...
|
||||||
|
outputLabel: CSV version of your JSON
|
||||||
|
|
||||||
|
invalidMessage: Provided JSON is not valid.
|
10
src/tools/json-to-csv/locales/zh.yml
Normal file
10
src/tools/json-to-csv/locales/zh.yml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
tools:
|
||||||
|
json-to-csv:
|
||||||
|
title: JSON 转换为 CSV
|
||||||
|
description: 将 JSON 转换为 CSV,并自动检测标题。
|
||||||
|
|
||||||
|
inputLabel: 您的原始 JSON
|
||||||
|
inputPlaceholder: 在此粘贴您的原始 JSON...
|
||||||
|
outputLabel: 您的 JSON 的 CSV 版本
|
||||||
|
|
||||||
|
invalidMessage: 提供的 JSON 不是有效的。
|
|
@ -1,10 +1,11 @@
|
||||||
import { Braces } from '@vicons/tabler';
|
import { Braces } from '@vicons/tabler';
|
||||||
import { defineTool } from '../tool';
|
import { defineTool } from '../tool';
|
||||||
|
import { translate as t } from '@/plugins/i18n.plugin';
|
||||||
|
|
||||||
export const tool = defineTool({
|
export const tool = defineTool({
|
||||||
name: 'JSON prettify and format',
|
name: t('tools.json-prettify.title'),
|
||||||
path: '/json-prettify',
|
path: '/json-prettify',
|
||||||
description: 'Prettify your JSON string to a human friendly readable format.',
|
description: t('tools.json-prettify.description'),
|
||||||
keywords: ['json', 'viewer', 'prettify', 'format'],
|
keywords: ['json', 'viewer', 'prettify', 'format'],
|
||||||
component: () => import('./json-viewer.vue'),
|
component: () => import('./json-viewer.vue'),
|
||||||
icon: Braces,
|
icon: Braces,
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { withDefaultOnError } from '@/utils/defaults';
|
||||||
import { useValidation } from '@/composable/validation';
|
import { useValidation } from '@/composable/validation';
|
||||||
import TextareaCopyable from '@/components/TextareaCopyable.vue';
|
import TextareaCopyable from '@/components/TextareaCopyable.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
const inputElement = ref<HTMLElement>();
|
const inputElement = ref<HTMLElement>();
|
||||||
|
|
||||||
const rawJson = useStorage('json-prettify:raw-json', '{"hello": "world", "foo": "bar"}');
|
const rawJson = useStorage('json-prettify:raw-json', '{"hello": "world", "foo": "bar"}');
|
||||||
|
@ -18,7 +19,7 @@ const rawJsonValidation = useValidation({
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
validator: v => v === '' || JSON5.parse(v),
|
validator: v => v === '' || JSON5.parse(v),
|
||||||
message: 'Provided JSON is not valid.',
|
message: t('tools.json-prettify.invalidMessage'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
@ -27,24 +28,24 @@ const rawJsonValidation = useValidation({
|
||||||
<template>
|
<template>
|
||||||
<div style="flex: 0 0 100%">
|
<div style="flex: 0 0 100%">
|
||||||
<div style="margin: 0 auto; max-width: 600px" flex justify-center gap-3>
|
<div style="margin: 0 auto; max-width: 600px" flex justify-center gap-3>
|
||||||
<n-form-item label="Sort keys :" label-placement="left" label-width="100">
|
<n-form-item :label="t('tools.json-prettify.sortKeys')" label-placement="left" label-width="100">
|
||||||
<n-switch v-model:value="sortKeys" />
|
<n-switch v-model:value="sortKeys" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="Indent size :" label-placement="left" label-width="100" :show-feedback="false">
|
<n-form-item :label="t('tools.json-prettify.indentSize')" label-placement="left" label-width="100" :show-feedback="false">
|
||||||
<n-input-number v-model:value="indentSize" min="0" max="10" style="width: 100px" />
|
<n-input-number v-model:value="indentSize" min="0" max="10" style="width: 100px" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<n-form-item
|
<n-form-item
|
||||||
label="Your raw JSON"
|
:label="t('tools.json-prettify.rawJSONLabel')"
|
||||||
:feedback="rawJsonValidation.message"
|
:feedback="rawJsonValidation.message"
|
||||||
:validation-status="rawJsonValidation.status"
|
:validation-status="rawJsonValidation.status"
|
||||||
>
|
>
|
||||||
<c-input-text
|
<c-input-text
|
||||||
ref="inputElement"
|
ref="inputElement"
|
||||||
v-model:value="rawJson"
|
v-model:value="rawJson"
|
||||||
placeholder="Paste your raw JSON here..."
|
:placeholder="t('tools.json-prettify.rawJSONPlaceholder')"
|
||||||
rows="20"
|
rows="20"
|
||||||
multiline
|
multiline
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
|
@ -54,7 +55,7 @@ const rawJsonValidation = useValidation({
|
||||||
monospace
|
monospace
|
||||||
/>
|
/>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="Prettified version of your JSON">
|
<n-form-item :label="t('tools.json-prettify.prettifyJSON')">
|
||||||
<TextareaCopyable :value="cleanJson" language="json" :follow-height-of="inputElement" />
|
<TextareaCopyable :value="cleanJson" language="json" :follow-height-of="inputElement" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</template>
|
</template>
|
||||||
|
|
12
src/tools/json-viewer/locales/en.yml
Normal file
12
src/tools/json-viewer/locales/en.yml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
tools:
|
||||||
|
json-prettify:
|
||||||
|
title: JSON prettify and format
|
||||||
|
description: Prettify your JSON string to a human friendly readable format.
|
||||||
|
|
||||||
|
sortKeys: 'Sort keys :'
|
||||||
|
indentSize: 'Indent size :'
|
||||||
|
rawJSONLabel: Your raw JSON
|
||||||
|
rawJSONPlaceholder: Paste your raw JSON here...
|
||||||
|
prettifyJSON: Prettified version of your JSON
|
||||||
|
|
||||||
|
invalidMessage: Provided JSON is not valid.
|
12
src/tools/json-viewer/locales/zh.yml
Normal file
12
src/tools/json-viewer/locales/zh.yml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
tools:
|
||||||
|
json-prettify:
|
||||||
|
title: JSON 格式化工具
|
||||||
|
description: 将您的 JSON 字符串美化为易读的格式。
|
||||||
|
|
||||||
|
sortKeys: '排序键:'
|
||||||
|
indentSize: '缩进大小:'
|
||||||
|
rawJSONLabel: 您的原始 JSON
|
||||||
|
rawJSONPlaceholder: 在此粘贴您的原始 JSON…
|
||||||
|
prettifyJSON: 您的 JSON 的美化版本
|
||||||
|
|
||||||
|
invalidMessage: 提供的 JSON 不是有效的。
|
|
@ -1,11 +1,11 @@
|
||||||
import { Qrcode } from '@vicons/tabler';
|
import { Qrcode } from '@vicons/tabler';
|
||||||
import { defineTool } from '../tool';
|
import { defineTool } from '../tool';
|
||||||
|
import { translate as t } from '@/plugins/i18n.plugin';
|
||||||
|
|
||||||
export const tool = defineTool({
|
export const tool = defineTool({
|
||||||
name: 'QR Code generator',
|
name: t('tools.qrcode-generator.title'),
|
||||||
path: '/qrcode-generator',
|
path: '/qrcode-generator',
|
||||||
description:
|
description: t('tools.qrcode-generator.description'),
|
||||||
'Generate and download QR-code for an url or just a text and customize the background and foreground colors.',
|
|
||||||
keywords: ['qr', 'code', 'generator', 'square', 'color', 'link', 'low', 'medium', 'quartile', 'high', 'transparent'],
|
keywords: ['qr', 'code', 'generator', 'square', 'color', 'link', 'low', 'medium', 'quartile', 'high', 'transparent'],
|
||||||
component: () => import('./qr-code-generator.vue'),
|
component: () => import('./qr-code-generator.vue'),
|
||||||
icon: Qrcode,
|
icon: Qrcode,
|
||||||
|
|
16
src/tools/qr-code-generator/locales/en.yml
Normal file
16
src/tools/qr-code-generator/locales/en.yml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
tools:
|
||||||
|
qrcode-generator:
|
||||||
|
title: QR Code generator
|
||||||
|
description: Generate and download QR-code for an url or just a text and customize the background and foreground colors.
|
||||||
|
|
||||||
|
low: low
|
||||||
|
medium: medium
|
||||||
|
quartile: quartile
|
||||||
|
high: high
|
||||||
|
|
||||||
|
textLabel: 'Text:'
|
||||||
|
textPlaceholder: 'Your link or text...'
|
||||||
|
foreground: 'Foreground color:'
|
||||||
|
background: 'Background color:'
|
||||||
|
errorCorrectionLevels: 'Error resistance:'
|
||||||
|
downloadBtn: Download qr-code
|
16
src/tools/qr-code-generator/locales/zh.yml
Normal file
16
src/tools/qr-code-generator/locales/zh.yml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
tools:
|
||||||
|
qrcode-generator:
|
||||||
|
title: 二维码生成器
|
||||||
|
description: 生成并下载一个网址或文本的二维码,并自定义背景和前景颜色。
|
||||||
|
|
||||||
|
low: 低
|
||||||
|
medium: 中
|
||||||
|
quartile: 四分之一
|
||||||
|
high: 高
|
||||||
|
|
||||||
|
textLabel: '文本:'
|
||||||
|
textPlaceholder: '您的链接或文本...'
|
||||||
|
foreground: '前景色:'
|
||||||
|
background: '背景色:'
|
||||||
|
errorCorrectionLevels: '错误纠正等级:'
|
||||||
|
downloadBtn: 下载二维码
|
|
@ -3,11 +3,12 @@ import type { QRCodeErrorCorrectionLevel } from 'qrcode';
|
||||||
import { useQRCode } from './useQRCode';
|
import { useQRCode } from './useQRCode';
|
||||||
import { useDownloadFileFromBase64 } from '@/composable/downloadBase64';
|
import { useDownloadFileFromBase64 } from '@/composable/downloadBase64';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
const foreground = ref('#000000ff');
|
const foreground = ref('#000000ff');
|
||||||
const background = ref('#ffffffff');
|
const background = ref('#ffffffff');
|
||||||
const errorCorrectionLevel = ref<QRCodeErrorCorrectionLevel>('medium');
|
const errorCorrectionLevel = ref<QRCodeErrorCorrectionLevel>(t('tools.qrcode-generator.medium') as 'medium');
|
||||||
|
|
||||||
const errorCorrectionLevels = ['low', 'medium', 'quartile', 'high'];
|
const errorCorrectionLevels = [t('tools.qrcode-generator.low'), t('tools.qrcode-generator.medium'), t('tools.qrcode-generator.quartile'), t('tools.qrcode-generator.high')];
|
||||||
|
|
||||||
const text = ref('https://it-tools.tech');
|
const text = ref('https://it-tools.tech');
|
||||||
const { qrcode } = useQRCode({
|
const { qrcode } = useQRCode({
|
||||||
|
@ -32,23 +33,23 @@ const { download } = useDownloadFileFromBase64({ source: qrcode, filename: 'qr-c
|
||||||
label-position="left"
|
label-position="left"
|
||||||
label-width="130px"
|
label-width="130px"
|
||||||
label-align="right"
|
label-align="right"
|
||||||
label="Text:"
|
:label="t('tools.qrcode-generator.textLabel')"
|
||||||
multiline
|
multiline
|
||||||
rows="1"
|
rows="1"
|
||||||
autosize
|
autosize
|
||||||
placeholder="Your link or text..."
|
:placeholder="t('tools.qrcode-generator.textPlaceholder')"
|
||||||
mb-6
|
mb-6
|
||||||
/>
|
/>
|
||||||
<n-form label-width="130" label-placement="left">
|
<n-form label-width="130" label-placement="left">
|
||||||
<n-form-item label="Foreground color:">
|
<n-form-item :label="t('tools.qrcode-generator.foreground')">
|
||||||
<n-color-picker v-model:value="foreground" :modes="['hex']" />
|
<n-color-picker v-model:value="foreground" :modes="['hex']" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="Background color:">
|
<n-form-item :label="t('tools.qrcode-generator.background')">
|
||||||
<n-color-picker v-model:value="background" :modes="['hex']" />
|
<n-color-picker v-model:value="background" :modes="['hex']" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<c-select
|
<c-select
|
||||||
v-model:value="errorCorrectionLevel"
|
v-model:value="errorCorrectionLevel"
|
||||||
label="Error resistance:"
|
:label="t('tools.qrcode-generator.errorCorrectionLevels')"
|
||||||
label-position="left"
|
label-position="left"
|
||||||
label-width="130px"
|
label-width="130px"
|
||||||
label-align="right"
|
label-align="right"
|
||||||
|
@ -60,7 +61,7 @@ const { download } = useDownloadFileFromBase64({ source: qrcode, filename: 'qr-c
|
||||||
<div flex flex-col items-center gap-3>
|
<div flex flex-col items-center gap-3>
|
||||||
<n-image :src="qrcode" width="200" />
|
<n-image :src="qrcode" width="200" />
|
||||||
<c-button @click="download">
|
<c-button @click="download">
|
||||||
Download qr-code
|
{{ t('tools.qrcode-generator.downloadBtn') }}
|
||||||
</c-button>
|
</c-button>
|
||||||
</div>
|
</div>
|
||||||
</n-gi>
|
</n-gi>
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { Server } from '@vicons/tabler';
|
import { Server } from '@vicons/tabler';
|
||||||
import { defineTool } from '../tool';
|
import { defineTool } from '../tool';
|
||||||
|
import { translate as t } from '@/plugins/i18n.plugin';
|
||||||
|
|
||||||
export const tool = defineTool({
|
export const tool = defineTool({
|
||||||
name: 'Random port generator',
|
name: t('tools.random-port-generator.title'),
|
||||||
path: '/random-port-generator',
|
path: '/random-port-generator',
|
||||||
description: 'Generate random port numbers outside of the range of "known" ports (0-1023).',
|
description: t('tools.random-port-generator.description'),
|
||||||
keywords: ['system', 'port', 'lan', 'generator', 'random', 'development', 'computer'],
|
keywords: ['system', 'port', 'lan', 'generator', 'random', 'development', 'computer'],
|
||||||
component: () => import('./random-port-generator.vue'),
|
component: () => import('./random-port-generator.vue'),
|
||||||
icon: Server,
|
icon: Server,
|
||||||
|
|
8
src/tools/random-port-generator/locales/en.yml
Normal file
8
src/tools/random-port-generator/locales/en.yml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
tools:
|
||||||
|
random-port-generator:
|
||||||
|
title: Random port generator
|
||||||
|
description: Generate random port numbers outside of the range of "known" ports (0-1023).
|
||||||
|
|
||||||
|
copyBtn: Copy
|
||||||
|
refreshBtn: Refresh
|
||||||
|
copied: Port copied to the clipboard
|
8
src/tools/random-port-generator/locales/zh.yml
Normal file
8
src/tools/random-port-generator/locales/zh.yml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
tools:
|
||||||
|
random-port-generator:
|
||||||
|
title: 随机端口生成器
|
||||||
|
description: 生成在“已知”端口范围之外的随机端口号(0-1023)。
|
||||||
|
|
||||||
|
copyBtn: 复制
|
||||||
|
refreshBtn: 刷新
|
||||||
|
copied: 端口已复制到剪贴板
|
|
@ -3,9 +3,10 @@ import { generatePort } from './random-port-generator.model';
|
||||||
import { computedRefreshable } from '@/composable/computedRefreshable';
|
import { computedRefreshable } from '@/composable/computedRefreshable';
|
||||||
import { useCopy } from '@/composable/copy';
|
import { useCopy } from '@/composable/copy';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
const [port, refreshPort] = computedRefreshable(() => String(generatePort()));
|
const [port, refreshPort] = computedRefreshable(() => String(generatePort()));
|
||||||
|
|
||||||
const { copy } = useCopy({ source: port, text: 'Port copied to the clipboard' });
|
const { copy } = useCopy({ source: port, text: t('tools.random-port-generator.copied') });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -15,10 +16,10 @@ const { copy } = useCopy({ source: port, text: 'Port copied to the clipboard' })
|
||||||
</div>
|
</div>
|
||||||
<div flex justify-center gap-3>
|
<div flex justify-center gap-3>
|
||||||
<c-button @click="copy()">
|
<c-button @click="copy()">
|
||||||
Copy
|
{{ t('tools.random-port-generator.copyBtn') }}
|
||||||
</c-button>
|
</c-button>
|
||||||
<c-button @click="refreshPort">
|
<c-button @click="refreshPort">
|
||||||
Refresh
|
{{ t('tools.random-port-generator.refreshBtn') }}
|
||||||
</c-button>
|
</c-button>
|
||||||
</div>
|
</div>
|
||||||
</c-card>
|
</c-card>
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { Database } from '@vicons/tabler';
|
import { Database } from '@vicons/tabler';
|
||||||
import { defineTool } from '../tool';
|
import { defineTool } from '../tool';
|
||||||
|
import { translate as t } from '@/plugins/i18n.plugin';
|
||||||
|
|
||||||
export const tool = defineTool({
|
export const tool = defineTool({
|
||||||
name: 'SQL prettify and format',
|
name: t('tools.sql-prettify.title'),
|
||||||
path: '/sql-prettify',
|
path: '/sql-prettify',
|
||||||
description: 'Format and prettify your SQL queries online (it supports various SQL dialects).',
|
description: t('tools.sql-prettify.description'),
|
||||||
keywords: [
|
keywords: [
|
||||||
'sql',
|
'sql',
|
||||||
'prettify',
|
'prettify',
|
||||||
|
|
19
src/tools/sql-prettify/locales/en.yml
Normal file
19
src/tools/sql-prettify/locales/en.yml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
tools:
|
||||||
|
sql-prettify:
|
||||||
|
title: SQL prettify and format
|
||||||
|
description: Format and prettify your SQL queries online (it supports various SQL dialects).
|
||||||
|
|
||||||
|
dialect: Dialect
|
||||||
|
keywordCase: Keyword case
|
||||||
|
indentStyle: Indent style
|
||||||
|
sql: Standard SQL
|
||||||
|
upper: UPPERCASE
|
||||||
|
lower: lowercase
|
||||||
|
preserve: Preserve
|
||||||
|
standard: Standard
|
||||||
|
tabularLeft: Tabular left
|
||||||
|
tabularRight: Tabular right
|
||||||
|
|
||||||
|
inputLabel: Your SQL query
|
||||||
|
inputPlaceholder: Put your SQL query here...
|
||||||
|
outputLabel: Prettify version of your query
|
19
src/tools/sql-prettify/locales/zh.yml
Normal file
19
src/tools/sql-prettify/locales/zh.yml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
tools:
|
||||||
|
sql-prettify:
|
||||||
|
title: SQL 格式化工具
|
||||||
|
description: 在线格式化和美化您的 SQL 查询(支持各种 SQL 方言)。
|
||||||
|
|
||||||
|
dialect: 方言
|
||||||
|
keywordCase: 关键词大小写
|
||||||
|
indentStyle: 缩进样式
|
||||||
|
sql: 标准 SQL
|
||||||
|
upper: 大写
|
||||||
|
lower: 小写
|
||||||
|
preserve: 保留原样
|
||||||
|
standard: 标准
|
||||||
|
tabularLeft: 左对齐
|
||||||
|
tabularRight: 右对齐
|
||||||
|
|
||||||
|
inputLabel: 您的 SQL 查询
|
||||||
|
inputPlaceholder: 在此处放置您的 SQL 查询...
|
||||||
|
outputLabel: SQL 查询的美化版本
|
|
@ -3,6 +3,7 @@ import { type FormatOptionsWithLanguage, format as formatSQL } from 'sql-formatt
|
||||||
import TextareaCopyable from '@/components/TextareaCopyable.vue';
|
import TextareaCopyable from '@/components/TextareaCopyable.vue';
|
||||||
import { useStyleStore } from '@/stores/style.store';
|
import { useStyleStore } from '@/stores/style.store';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
const inputElement = ref<HTMLElement>();
|
const inputElement = ref<HTMLElement>();
|
||||||
const styleStore = useStyleStore();
|
const styleStore = useStyleStore();
|
||||||
const config = reactive<FormatOptionsWithLanguage>({
|
const config = reactive<FormatOptionsWithLanguage>({
|
||||||
|
@ -23,7 +24,7 @@ const prettySQL = computed(() => formatSQL(rawSQL.value, config));
|
||||||
<c-select
|
<c-select
|
||||||
v-model:value="config.language"
|
v-model:value="config.language"
|
||||||
flex-1
|
flex-1
|
||||||
label="Dialect"
|
:label="t('tools.sql-prettify.dialect')"
|
||||||
:options="[
|
:options="[
|
||||||
{ label: 'GCP BigQuery', value: 'bigquery' },
|
{ label: 'GCP BigQuery', value: 'bigquery' },
|
||||||
{ label: 'IBM DB2', value: 'db2' },
|
{ label: 'IBM DB2', value: 'db2' },
|
||||||
|
@ -35,37 +36,37 @@ const prettySQL = computed(() => formatSQL(rawSQL.value, config));
|
||||||
{ label: 'PostgreSQL', value: 'postgresql' },
|
{ label: 'PostgreSQL', value: 'postgresql' },
|
||||||
{ label: 'Amazon Redshift', value: 'redshift' },
|
{ label: 'Amazon Redshift', value: 'redshift' },
|
||||||
{ label: 'Spark', value: 'spark' },
|
{ label: 'Spark', value: 'spark' },
|
||||||
{ label: 'Standard SQL', value: 'sql' },
|
{ label: t('tools.sql-prettify.sql'), value: 'sql' },
|
||||||
{ label: 'sqlite', value: 'sqlite' },
|
{ label: 'sqlite', value: 'sqlite' },
|
||||||
{ label: 'SQL Server Transact-SQL', value: 'tsql' },
|
{ label: 'SQL Server Transact-SQL', value: 'tsql' },
|
||||||
]"
|
]"
|
||||||
/>
|
/>
|
||||||
<c-select
|
<c-select
|
||||||
v-model:value="config.keywordCase" label="Keyword case"
|
v-model:value="config.keywordCase" :label="t('tools.sql-prettify.keywordCase')"
|
||||||
flex-1
|
flex-1
|
||||||
:options="[
|
:options="[
|
||||||
{ label: 'UPPERCASE', value: 'upper' },
|
{ label: t('tools.sql-prettify.upper'), value: 'upper' },
|
||||||
{ label: 'lowercase', value: 'lower' },
|
{ label: t('tools.sql-prettify.lower'), value: 'lower' },
|
||||||
{ label: 'Preserve', value: 'preserve' },
|
{ label: t('tools.sql-prettify.preserve'), value: 'preserve' },
|
||||||
]"
|
]"
|
||||||
/>
|
/>
|
||||||
<c-select
|
<c-select
|
||||||
v-model:value="config.indentStyle" label="Indent style"
|
v-model:value="config.indentStyle" :label="t('tools.sql-prettify.indentStyle')"
|
||||||
flex-1
|
flex-1
|
||||||
:options="[
|
:options="[
|
||||||
{ label: 'Standard', value: 'standard' },
|
{ label: t('tools.sql-prettify.standard'), value: 'standard' },
|
||||||
{ label: 'Tabular left', value: 'tabularLeft' },
|
{ label: t('tools.sql-prettify.tabularLeft'), value: 'tabularLeft' },
|
||||||
{ label: 'Tabular right', value: 'tabularRight' },
|
{ label: t('tools.sql-prettify.tabularRight'), value: 'tabularRight' },
|
||||||
]"
|
]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<n-form-item label="Your SQL query">
|
<n-form-item :label="t('tools.sql-prettify.inputLabel')">
|
||||||
<c-input-text
|
<c-input-text
|
||||||
ref="inputElement"
|
ref="inputElement"
|
||||||
v-model:value="rawSQL"
|
v-model:value="rawSQL"
|
||||||
placeholder="Put your SQL query here..."
|
:placeholder="t('tools.sql-prettify.inputPlaceholder')"
|
||||||
rows="20"
|
rows="20"
|
||||||
multiline
|
multiline
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
|
@ -75,7 +76,7 @@ const prettySQL = computed(() => formatSQL(rawSQL.value, config));
|
||||||
monospace
|
monospace
|
||||||
/>
|
/>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="Prettify version of your query">
|
<n-form-item :label="t('tools.sql-prettify.outputLabel')">
|
||||||
<TextareaCopyable :value="prettySQL" language="sql" :follow-height-of="inputElement" />
|
<TextareaCopyable :value="prettySQL" language="sql" :follow-height-of="inputElement" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { ImageOutlined } from '@vicons/material';
|
import { ImageOutlined } from '@vicons/material';
|
||||||
import { defineTool } from '../tool';
|
import { defineTool } from '../tool';
|
||||||
|
import { translate as t } from '@/plugins/i18n.plugin';
|
||||||
|
|
||||||
export const tool = defineTool({
|
export const tool = defineTool({
|
||||||
name: 'SVG placeholder generator',
|
name: t('tools.svg-placeholder-generator.title'),
|
||||||
path: '/svg-placeholder-generator',
|
path: '/svg-placeholder-generator',
|
||||||
description: 'Generate svg images to use as placeholder in your applications.',
|
description: t('tools.svg-placeholder-generator.description'),
|
||||||
keywords: ['svg', 'placeholder', 'generator', 'image', 'size', 'mockup'],
|
keywords: ['svg', 'placeholder', 'generator', 'image', 'size', 'mockup'],
|
||||||
component: () => import('./svg-placeholder-generator.vue'),
|
component: () => import('./svg-placeholder-generator.vue'),
|
||||||
icon: ImageOutlined,
|
icon: ImageOutlined,
|
||||||
|
|
21
src/tools/svg-placeholder-generator/locales/en.yml
Normal file
21
src/tools/svg-placeholder-generator/locales/en.yml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
tools:
|
||||||
|
svg-placeholder-generator:
|
||||||
|
title: SVG placeholder generator
|
||||||
|
description: Generate svg images to use as placeholder in your applications.
|
||||||
|
|
||||||
|
widthLabel: 'Width (in px)'
|
||||||
|
widthPlaceholder: 'SVG width...'
|
||||||
|
heightLabel: 'Height (in px)'
|
||||||
|
heightPlaceholder: 'SVG height...'
|
||||||
|
backgroundLabel: 'Background'
|
||||||
|
textColorLabel: 'Text color'
|
||||||
|
fontSizeLabel: 'Font size'
|
||||||
|
fontSizePlaceholder: 'Font size...'
|
||||||
|
customTextLabel: 'Custom text'
|
||||||
|
customTextPlaceholder: 'Default is {width}x{height}'
|
||||||
|
useExactSize: 'Use exact size'
|
||||||
|
svgString: 'SVG HTML element'
|
||||||
|
base64: 'SVG in Base64'
|
||||||
|
copySvg: 'Copy svg'
|
||||||
|
copyBase64: 'Copy base64'
|
||||||
|
downloadSvg: 'Download svg'
|
21
src/tools/svg-placeholder-generator/locales/zh.yml
Normal file
21
src/tools/svg-placeholder-generator/locales/zh.yml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
tools:
|
||||||
|
svg-placeholder-generator:
|
||||||
|
title: SVG 占位符生成器
|
||||||
|
description: 生成 SVG 图像,用作应用程序中的占位符。
|
||||||
|
|
||||||
|
widthLabel: '宽度(以像素为单位)'
|
||||||
|
widthPlaceholder: 'SVG 宽度...'
|
||||||
|
heightLabel: '高度(以像素为单位)'
|
||||||
|
heightPlaceholder: 'SVG 高度...'
|
||||||
|
backgroundLabel: '背景'
|
||||||
|
textColorLabel: '文本颜色'
|
||||||
|
fontSizeLabel: '字体大小'
|
||||||
|
fontSizePlaceholder: '字体大小...'
|
||||||
|
customTextLabel: '自定义文本'
|
||||||
|
customTextPlaceholder: '默认为 {width}x{height}'
|
||||||
|
useExactSize: '使用精确尺寸'
|
||||||
|
svgString: 'SVG HTML 元素'
|
||||||
|
base64: 'Base64 编码的 SVG'
|
||||||
|
copySvg: '复制 SVG'
|
||||||
|
copyBase64: '复制 Base64'
|
||||||
|
downloadSvg: '下载 SVG'
|
|
@ -4,6 +4,7 @@ import { useCopy } from '@/composable/copy';
|
||||||
import { useDownloadFileFromBase64 } from '@/composable/downloadBase64';
|
import { useDownloadFileFromBase64 } from '@/composable/downloadBase64';
|
||||||
import { textToBase64 } from '@/utils/base64';
|
import { textToBase64 } from '@/utils/base64';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
const width = ref(600);
|
const width = ref(600);
|
||||||
const height = ref(350);
|
const height = ref(350);
|
||||||
const fontSize = ref(26);
|
const fontSize = ref(26);
|
||||||
|
@ -35,57 +36,57 @@ const { download } = useDownloadFileFromBase64({ source: base64 });
|
||||||
<div>
|
<div>
|
||||||
<n-form label-placement="left" label-width="100">
|
<n-form label-placement="left" label-width="100">
|
||||||
<div flex gap-3>
|
<div flex gap-3>
|
||||||
<n-form-item label="Width (in px)" flex-1>
|
<n-form-item :label="t('tools.svg-placeholder-generator.widthLabel')" flex-1>
|
||||||
<n-input-number v-model:value="width" placeholder="SVG width..." min="1" />
|
<n-input-number v-model:value="width" :placeholder="t('tools.svg-placeholder-generator.widthPlaceholder')" min="1" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="Background" flex-1>
|
<n-form-item :label="t('tools.svg-placeholder-generator.backgroundLabel')" flex-1>
|
||||||
<n-color-picker v-model:value="bgColor" :modes="['hex']" />
|
<n-color-picker v-model:value="bgColor" :modes="['hex']" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</div>
|
</div>
|
||||||
<div flex gap-3>
|
<div flex gap-3>
|
||||||
<n-form-item label="Height (in px)" flex-1>
|
<n-form-item :label="t('tools.svg-placeholder-generator.heightLabel')" flex-1>
|
||||||
<n-input-number v-model:value="height" placeholder="SVG height..." min="1" />
|
<n-input-number v-model:value="height" :placeholder="t('tools.svg-placeholder-generator.heightPlaceholder')" min="1" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="Text color" flex-1>
|
<n-form-item :label="t('tools.svg-placeholder-generator.textColorLabel')" flex-1>
|
||||||
<n-color-picker v-model:value="fgColor" :modes="['hex']" />
|
<n-color-picker v-model:value="fgColor" :modes="['hex']" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</div>
|
</div>
|
||||||
<div flex gap-3>
|
<div flex gap-3>
|
||||||
<n-form-item label="Font size" flex-1>
|
<n-form-item :label="t('tools.svg-placeholder-generator.fontSizeLabel')" flex-1>
|
||||||
<n-input-number v-model:value="fontSize" placeholder="Font size..." min="1" />
|
<n-input-number v-model:value="fontSize" :placeholder="t('tools.svg-placeholder-generator.fontSizePlaceholder')" min="1" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
|
|
||||||
<c-input-text
|
<c-input-text
|
||||||
v-model:value="customText"
|
v-model:value="customText"
|
||||||
label="Custom text"
|
:label="t('tools.svg-placeholder-generator.customTextLabel')"
|
||||||
:placeholder="`Default is ${width}x${height}`"
|
:placeholder="t('tools.svg-placeholder-generator.customTextPlaceholder', { width, height })"
|
||||||
label-position="left"
|
label-position="left"
|
||||||
label-width="100px"
|
label-width="100px"
|
||||||
label-align="right"
|
label-align="right"
|
||||||
flex-1
|
flex-1
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<n-form-item label="Use exact size" label-placement="left">
|
<n-form-item :label="t('tools.svg-placeholder-generator.useExactSize')" label-placement="left">
|
||||||
<n-switch v-model:value="useExactSize" />
|
<n-switch v-model:value="useExactSize" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-form>
|
</n-form>
|
||||||
|
|
||||||
<n-form-item label="SVG HTML element">
|
<n-form-item :label="t('tools.svg-placeholder-generator.svgString')">
|
||||||
<TextareaCopyable :value="svgString" copy-placement="none" />
|
<TextareaCopyable :value="svgString" copy-placement="none" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="SVG in Base64">
|
<n-form-item :label="t('tools.svg-placeholder-generator.base64')">
|
||||||
<TextareaCopyable :value="base64" copy-placement="none" />
|
<TextareaCopyable :value="base64" copy-placement="none" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
|
|
||||||
<div flex justify-center gap-3>
|
<div flex justify-center gap-3>
|
||||||
<c-button @click="copySVG()">
|
<c-button @click="copySVG()">
|
||||||
Copy svg
|
{{ t('tools.svg-placeholder-generator.copySvg') }}
|
||||||
</c-button>
|
</c-button>
|
||||||
<c-button @click="copyBase64()">
|
<c-button @click="copyBase64()">
|
||||||
Copy base64
|
{{ t('tools.svg-placeholder-generator.copyBase64') }}
|
||||||
</c-button>
|
</c-button>
|
||||||
<c-button @click="download()">
|
<c-button @click="download()">
|
||||||
Download svg
|
{{ t('tools.svg-placeholder-generator.downloadSvg') }}
|
||||||
</c-button>
|
</c-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { Qrcode } from '@vicons/tabler';
|
import { Qrcode } from '@vicons/tabler';
|
||||||
import { defineTool } from '../tool';
|
import { defineTool } from '../tool';
|
||||||
|
import { translate as t } from '@/plugins/i18n.plugin';
|
||||||
|
|
||||||
export const tool = defineTool({
|
export const tool = defineTool({
|
||||||
name: 'WiFi QR Code generator',
|
name: t('tools.wifi-qrcode-generator.title'),
|
||||||
path: '/wifi-qrcode-generator',
|
path: '/wifi-qrcode-generator',
|
||||||
description:
|
description: t('tools.wifi-qrcode-generator.description'),
|
||||||
'Generate and download QR-codes for quick connections to WiFi networks.',
|
|
||||||
keywords: ['qr', 'code', 'generator', 'square', 'color', 'link', 'low', 'medium', 'quartile', 'high', 'transparent', 'wifi'],
|
keywords: ['qr', 'code', 'generator', 'square', 'color', 'link', 'low', 'medium', 'quartile', 'high', 'transparent', 'wifi'],
|
||||||
component: () => import('./wifi-qr-code-generator.vue'),
|
component: () => import('./wifi-qr-code-generator.vue'),
|
||||||
icon: Qrcode,
|
icon: Qrcode,
|
||||||
|
|
20
src/tools/wifi-qr-code-generator/locales/en.yml
Normal file
20
src/tools/wifi-qr-code-generator/locales/en.yml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
tools:
|
||||||
|
wifi-qrcode-generator:
|
||||||
|
title: WiFi QR Code generator
|
||||||
|
description: Generate and download QR-codes for quick connections to WiFi networks.
|
||||||
|
|
||||||
|
encryption: Encryption method
|
||||||
|
ssidLabel: 'SSID:'
|
||||||
|
ssidPlaceholder: 'Your WiFi SSID...'
|
||||||
|
hiddenSSID: Hidden SSID
|
||||||
|
passwordLabel: 'Password:'
|
||||||
|
passwordPlaceholder: 'Your WiFi Password...'
|
||||||
|
eapMethod: EAP method
|
||||||
|
eapIdentityLabel: 'Identity:'
|
||||||
|
eapIdentityPlaceholder: 'Your EAP Identity...'
|
||||||
|
anonymous: 'Anonymous?'
|
||||||
|
eapPhase2Method: 'EAP Phase 2 method'
|
||||||
|
foreground: 'Foreground color:'
|
||||||
|
background: 'Background color:'
|
||||||
|
wifiQrcode: wifi-qrcode
|
||||||
|
downloadBtn: Download qr-code
|
20
src/tools/wifi-qr-code-generator/locales/zh.yml
Normal file
20
src/tools/wifi-qr-code-generator/locales/zh.yml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
tools:
|
||||||
|
wifi-qrcode-generator:
|
||||||
|
title: WiFi 二维码生成器
|
||||||
|
description: 生成并下载用于快速连接 WiFi 网络的二维码。
|
||||||
|
|
||||||
|
encryption: 加密方法
|
||||||
|
ssidLabel: 'SSID:'
|
||||||
|
ssidPlaceholder: '您的 WiFi SSID...'
|
||||||
|
hiddenSSID: 隐藏 SSID
|
||||||
|
passwordLabel: '密码:'
|
||||||
|
passwordPlaceholder: '您的 WiFi 密码...'
|
||||||
|
eapMethod: EAP 方法
|
||||||
|
eapIdentityLabel: '身份:'
|
||||||
|
eapIdentityPlaceholder: '您的 EAP 身份...'
|
||||||
|
anonymous: '匿名?'
|
||||||
|
eapPhase2Method: 'EAP 第2阶段方法'
|
||||||
|
foreground: '前景色:'
|
||||||
|
background: '背景色:'
|
||||||
|
wifiQrcode: WiFi 二维码
|
||||||
|
downloadBtn: 下载二维码
|
|
@ -6,6 +6,7 @@ import {
|
||||||
} from './useQRCode';
|
} from './useQRCode';
|
||||||
import { useDownloadFileFromBase64 } from '@/composable/downloadBase64';
|
import { useDownloadFileFromBase64 } from '@/composable/downloadBase64';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
const foreground = ref('#000000ff');
|
const foreground = ref('#000000ff');
|
||||||
const background = ref('#ffffffff');
|
const background = ref('#ffffffff');
|
||||||
|
|
||||||
|
@ -42,7 +43,7 @@ const { download } = useDownloadFileFromBase64({ source: qrcode, filename: 'qr-c
|
||||||
<c-select
|
<c-select
|
||||||
v-model:value="encryption"
|
v-model:value="encryption"
|
||||||
mb-4
|
mb-4
|
||||||
label="Encryption method"
|
:label="t('tools.wifi-qrcode-generator.encryption')"
|
||||||
default-value="WPA"
|
default-value="WPA"
|
||||||
label-position="left"
|
label-position="left"
|
||||||
label-width="130px"
|
label-width="130px"
|
||||||
|
@ -72,14 +73,13 @@ const { download } = useDownloadFileFromBase64({ source: qrcode, filename: 'qr-c
|
||||||
label-position="left"
|
label-position="left"
|
||||||
label-width="130px"
|
label-width="130px"
|
||||||
label-align="right"
|
label-align="right"
|
||||||
label="SSID:"
|
:label="t('tools.wifi-qrcode-generator.ssidLabel')"
|
||||||
rows="1"
|
rows="1"
|
||||||
autosize
|
autosize
|
||||||
placeholder="Your WiFi SSID..."
|
:placeholder="t('tools.wifi-qrcode-generator.ssidPlaceholder')"
|
||||||
mb-6
|
|
||||||
/>
|
/>
|
||||||
<n-checkbox v-model:checked="isHiddenSSID">
|
<n-checkbox v-model:checked="isHiddenSSID" w-40>
|
||||||
Hidden SSID
|
{{ t('tools.wifi-qrcode-generator.hiddenSSID') }}
|
||||||
</n-checkbox>
|
</n-checkbox>
|
||||||
</div>
|
</div>
|
||||||
<c-input-text
|
<c-input-text
|
||||||
|
@ -88,17 +88,17 @@ const { download } = useDownloadFileFromBase64({ source: qrcode, filename: 'qr-c
|
||||||
label-position="left"
|
label-position="left"
|
||||||
label-width="130px"
|
label-width="130px"
|
||||||
label-align="right"
|
label-align="right"
|
||||||
label="Password:"
|
:label="t('tools.wifi-qrcode-generator.passwordLabel')"
|
||||||
rows="1"
|
rows="1"
|
||||||
autosize
|
autosize
|
||||||
type="password"
|
type="password"
|
||||||
placeholder="Your WiFi Password..."
|
:placeholder="t('tools.wifi-qrcode-generator.passwordPlaceholder')"
|
||||||
mb-6
|
mb-6
|
||||||
/>
|
/>
|
||||||
<c-select
|
<c-select
|
||||||
v-if="encryption === 'WPA2-EAP'"
|
v-if="encryption === 'WPA2-EAP'"
|
||||||
v-model:value="eapMethod"
|
v-model:value="eapMethod"
|
||||||
label="EAP method"
|
:label="t('tools.wifi-qrcode-generator.eapMethod')"
|
||||||
label-position="left"
|
label-position="left"
|
||||||
label-width="130px"
|
label-width="130px"
|
||||||
label-align="right"
|
label-align="right"
|
||||||
|
@ -111,20 +111,19 @@ const { download } = useDownloadFileFromBase64({ source: qrcode, filename: 'qr-c
|
||||||
label-position="left"
|
label-position="left"
|
||||||
label-width="130px"
|
label-width="130px"
|
||||||
label-align="right"
|
label-align="right"
|
||||||
label="Identity:"
|
:label="t('tools.wifi-qrcode-generator.eapIdentityLabel')"
|
||||||
rows="1"
|
rows="1"
|
||||||
autosize
|
autosize
|
||||||
placeholder="Your EAP Identity..."
|
:placeholder="t('tools.wifi-qrcode-generator.eapIdentityPlaceholder')"
|
||||||
mb-6
|
|
||||||
/>
|
/>
|
||||||
<n-checkbox v-model:checked="eapAnonymous">
|
<n-checkbox v-model:checked="eapAnonymous" w-40>
|
||||||
Anonymous?
|
{{ t('tools.wifi-qrcode-generator.anonymous') }}
|
||||||
</n-checkbox>
|
</n-checkbox>
|
||||||
</div>
|
</div>
|
||||||
<c-select
|
<c-select
|
||||||
v-if="encryption === 'WPA2-EAP'"
|
v-if="encryption === 'WPA2-EAP'"
|
||||||
v-model:value="eapPhase2Method"
|
v-model:value="eapPhase2Method"
|
||||||
label="EAP Phase 2 method"
|
:label="t('tools.wifi-qrcode-generator.eapPhase2Method')"
|
||||||
label-position="left"
|
label-position="left"
|
||||||
label-width="130px"
|
label-width="130px"
|
||||||
label-align="right"
|
label-align="right"
|
||||||
|
@ -132,19 +131,19 @@ const { download } = useDownloadFileFromBase64({ source: qrcode, filename: 'qr-c
|
||||||
searchable mb-4
|
searchable mb-4
|
||||||
/>
|
/>
|
||||||
<n-form label-width="130" label-placement="left">
|
<n-form label-width="130" label-placement="left">
|
||||||
<n-form-item label="Foreground color:">
|
<n-form-item :label="t('tools.wifi-qrcode-generator.foreground')">
|
||||||
<n-color-picker v-model:value="foreground" :modes="['hex']" />
|
<n-color-picker v-model:value="foreground" :modes="['hex']" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="Background color:">
|
<n-form-item :label="t('tools.wifi-qrcode-generator.background')">
|
||||||
<n-color-picker v-model:value="background" :modes="['hex']" />
|
<n-color-picker v-model:value="background" :modes="['hex']" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-form>
|
</n-form>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="qrcode">
|
<div v-if="qrcode">
|
||||||
<div flex flex-col items-center gap-3>
|
<div flex flex-col items-center gap-3>
|
||||||
<img alt="wifi-qrcode" :src="qrcode" width="200">
|
<img :alt="t('tools.wifi-qrcode-generator.wifiQrcode')" :src="qrcode" width="200">
|
||||||
<c-button @click="download">
|
<c-button @click="download">
|
||||||
Download qr-code
|
{{ t('tools.wifi-qrcode-generator.downloadBtn') }}
|
||||||
</c-button>
|
</c-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { Code } from '@vicons/tabler';
|
import { Code } from '@vicons/tabler';
|
||||||
import { defineTool } from '../tool';
|
import { defineTool } from '../tool';
|
||||||
|
import { translate as t } from '@/plugins/i18n.plugin';
|
||||||
|
|
||||||
export const tool = defineTool({
|
export const tool = defineTool({
|
||||||
name: 'XML formatter',
|
name: t('tools.xml-formatter.title'),
|
||||||
path: '/xml-formatter',
|
path: '/xml-formatter',
|
||||||
description: 'Prettify your XML string to a human friendly readable format.',
|
description: t('tools.xml-formatter.description'),
|
||||||
keywords: ['xml', 'prettify', 'format'],
|
keywords: ['xml', 'prettify', 'format'],
|
||||||
component: () => import('./xml-formatter.vue'),
|
component: () => import('./xml-formatter.vue'),
|
||||||
icon: Code,
|
icon: Code,
|
||||||
|
|
12
src/tools/xml-formatter/locales/en.yml
Normal file
12
src/tools/xml-formatter/locales/en.yml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
tools:
|
||||||
|
xml-formatter:
|
||||||
|
title: XML formatter
|
||||||
|
description: Prettify your XML string to a human friendly readable format.
|
||||||
|
|
||||||
|
collapseContent: 'Collapse content:'
|
||||||
|
indentSize: 'Indent size:'
|
||||||
|
inputLabel: Your XML
|
||||||
|
inputPlaceholder: Paste your XML here...
|
||||||
|
outputLabel: Formatted XML from your XML
|
||||||
|
|
||||||
|
invalidMessage: Provided XML is not valid.
|
12
src/tools/xml-formatter/locales/zh.yml
Normal file
12
src/tools/xml-formatter/locales/zh.yml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
tools:
|
||||||
|
xml-formatter:
|
||||||
|
title: XML 格式化工具
|
||||||
|
description: 将您的 XML 字符串美化为易读的人类友好格式。
|
||||||
|
|
||||||
|
collapseContent: '折叠内容:'
|
||||||
|
indentSize: '缩进大小:'
|
||||||
|
inputLabel: 您的 XML
|
||||||
|
inputPlaceholder: 在此粘贴您的 XML...
|
||||||
|
outputLabel: 根据您的 XML 格式化的 XML
|
||||||
|
|
||||||
|
invalidMessage: 提供的 XML 不合法。
|
|
@ -2,6 +2,7 @@
|
||||||
import { formatXml, isValidXML } from './xml-formatter.service';
|
import { formatXml, isValidXML } from './xml-formatter.service';
|
||||||
import type { UseValidationRule } from '@/composable/validation';
|
import type { UseValidationRule } from '@/composable/validation';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
const defaultValue = '<hello><world>foo</world><world>bar</world></hello>';
|
const defaultValue = '<hello><world>foo</world><world>bar</world></hello>';
|
||||||
const indentSize = useStorage('xml-formatter:indent-size', 2);
|
const indentSize = useStorage('xml-formatter:indent-size', 2);
|
||||||
const collapseContent = useStorage('xml-formatter:collapse-content', true);
|
const collapseContent = useStorage('xml-formatter:collapse-content', true);
|
||||||
|
@ -17,7 +18,7 @@ function transformer(value: string) {
|
||||||
const rules: UseValidationRule<string>[] = [
|
const rules: UseValidationRule<string>[] = [
|
||||||
{
|
{
|
||||||
validator: isValidXML,
|
validator: isValidXML,
|
||||||
message: 'Provided XML is not valid.',
|
message: t('tools.xml-formatter.invalidMessage'),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
|
@ -25,19 +26,19 @@ const rules: UseValidationRule<string>[] = [
|
||||||
<template>
|
<template>
|
||||||
<div important:flex-full important:flex-shrink-0 important:flex-grow-0>
|
<div important:flex-full important:flex-shrink-0 important:flex-grow-0>
|
||||||
<div flex justify-center>
|
<div flex justify-center>
|
||||||
<n-form-item label="Collapse content:" label-placement="left">
|
<n-form-item :label="t('tools.xml-formatter.collapseContent')" label-placement="left">
|
||||||
<n-switch v-model:value="collapseContent" />
|
<n-switch v-model:value="collapseContent" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="Indent size:" label-placement="left" label-width="100" :show-feedback="false">
|
<n-form-item :label="t('tools.xml-formatter.indentSize')" label-placement="left" label-width="100" :show-feedback="false">
|
||||||
<n-input-number v-model:value="indentSize" min="0" max="10" w-100px />
|
<n-input-number v-model:value="indentSize" min="0" max="10" w-100px />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<format-transformer
|
<format-transformer
|
||||||
input-label="Your XML"
|
:input-label="t('tools.xml-formatter.inputLabel')"
|
||||||
input-placeholder="Paste your XML here..."
|
:input-placeholder="t('tools.xml-formatter.inputPlaceholder')"
|
||||||
output-label="Formatted XML from your XML"
|
:output-label="t('tools.xml-formatter.outputLabel')"
|
||||||
output-language="xml"
|
output-language="xml"
|
||||||
:input-validation-rules="rules"
|
:input-validation-rules="rules"
|
||||||
:transformer="transformer"
|
:transformer="transformer"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue