mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-05-05 05:47: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']
|
||||
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.zh': typeof import('./src/tools/git-memo/git-memo.content.zh.md')['default']
|
||||
HashText: typeof import('./src/tools/hash-text/hash-text.vue')['default']
|
||||
HmacGenerator: typeof import('./src/tools/hmac-generator/hmac-generator.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 medias = ref<Media[]>([]);
|
||||
const currentCamera = ref(cameras.value[0]?.deviceId);
|
||||
|
@ -106,20 +108,19 @@ function downloadMedia({ type, value, createdAt }: Media) {
|
|||
<template>
|
||||
<div>
|
||||
<c-card v-if="!isSupported">
|
||||
Your browser does not support recording video from camera
|
||||
{{ t('tools.camera-recorder.unSupported') }}
|
||||
</c-card>
|
||||
|
||||
<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>
|
||||
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).
|
||||
{{ t('tools.camera-recorder.permissionCannotBePrompted') }}
|
||||
</c-alert>
|
||||
|
||||
<div v-else mt-4 flex justify-center>
|
||||
<c-button @click="requestPermissions">
|
||||
Grant permission
|
||||
{{ t('tools.camera-recorder.grantPermission') }}
|
||||
</c-button>
|
||||
</div>
|
||||
</c-card>
|
||||
|
@ -130,24 +131,24 @@ function downloadMedia({ type, value, createdAt }: Media) {
|
|||
v-model:value="currentCamera"
|
||||
label-position="left"
|
||||
label-width="60px"
|
||||
label="Video:"
|
||||
:label="t('tools.camera-recorder.cameraLabel')"
|
||||
:options="cameras.map(({ deviceId, label }) => ({ value: deviceId, label }))"
|
||||
placeholder="Select camera"
|
||||
:placeholder="t('tools.camera-recorder.cameraPlaceholder')"
|
||||
/>
|
||||
<c-select
|
||||
v-if="currentMicrophone && microphones.length > 0"
|
||||
v-model:value="currentMicrophone"
|
||||
label="Audio:"
|
||||
:label="t('tools.camera-recorder.microphoneLabel')"
|
||||
label-position="left"
|
||||
label-width="60px"
|
||||
:options="microphones.map(({ deviceId, label }) => ({ value: deviceId, label }))"
|
||||
placeholder="Select microphone"
|
||||
:placeholder="t('tools.camera-recorder.microphonePlaceholder')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="!isMediaStreamAvailable" mt-3 flex justify-center>
|
||||
<c-button type="primary" @click="start">
|
||||
Start webcam
|
||||
{{ t('tools.camera-recorder.startWebcam') }}
|
||||
</c-button>
|
||||
</div>
|
||||
|
||||
|
@ -159,32 +160,32 @@ function downloadMedia({ type, value, createdAt }: Media) {
|
|||
<div flex items-center justify-between gap-2>
|
||||
<c-button :disabled="!isMediaStreamAvailable" @click="takeScreenshot">
|
||||
<span mr-2> <icon-mdi-camera /></span>
|
||||
Take screenshot
|
||||
{{ t('tools.camera-recorder.takeScreenshot') }}
|
||||
</c-button>
|
||||
|
||||
<div v-if="isRecordingSupported" flex justify-center gap-2>
|
||||
<c-button v-if="recordingState === 'stopped'" @click="startRecording">
|
||||
<span mr-2> <icon-mdi-video /></span>
|
||||
Start recording
|
||||
{{ t('tools.camera-recorder.startRecording') }}
|
||||
</c-button>
|
||||
|
||||
<c-button v-if="recordingState === 'recording'" @click="pauseRecording">
|
||||
<span mr-2> <icon-mdi-pause /></span>
|
||||
Pause
|
||||
{{ t('tools.camera-recorder.pause') }}
|
||||
</c-button>
|
||||
|
||||
<c-button v-if="recordingState === 'paused'" @click="resumeRecording">
|
||||
<span mr-2> <icon-mdi-play /></span>
|
||||
Resume
|
||||
{{ t('tools.camera-recorder.resume') }}
|
||||
</c-button>
|
||||
|
||||
<c-button v-if="recordingState !== 'stopped'" type="error" @click="stopRecording">
|
||||
<span mr-2> <icon-mdi-record /></span>
|
||||
Stop
|
||||
{{ t('tools.camera-recorder.stop') }}
|
||||
</c-button>
|
||||
</div>
|
||||
<div v-else italic op-60>
|
||||
Video recording is not supported in your browser
|
||||
{{ t('tools.camera-recorder.unSupportRecord') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -192,13 +193,13 @@ function downloadMedia({ type, value, createdAt }: Media) {
|
|||
|
||||
<div grid grid-cols-2 mt-5 gap-2>
|
||||
<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 />
|
||||
|
||||
<div flex items-center justify-between>
|
||||
<div font-bold>
|
||||
{{ type === 'image' ? 'Screenshot' : 'Video' }}
|
||||
{{ type === 'image' ? t('tools.camera-recorder.screenshot') : t('tools.camera-recorder.video') }}
|
||||
</div>
|
||||
|
||||
<div flex gap-2>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { Camera } from '@vicons/tabler';
|
||||
import { defineTool } from '../tool';
|
||||
import { translate as t } from '@/plugins/i18n.plugin';
|
||||
|
||||
export const tool = defineTool({
|
||||
name: 'Camera recorder',
|
||||
name: t('tools.camera-recorder.title'),
|
||||
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'],
|
||||
component: () => import('./camera-recorder.vue'),
|
||||
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';
|
||||
|
||||
const themeVars = useThemeVars();
|
||||
const { t } = useI18n();
|
||||
|
||||
const scopes: { scope: Scope; title: string }[] = [
|
||||
{ scope: 'read', title: 'Read (4)' },
|
||||
{ scope: 'write', title: 'Write (2)' },
|
||||
{ scope: 'execute', title: 'Execute (1)' },
|
||||
{ scope: 'read', title: t('tools.chmod-calculator.read') },
|
||||
{ scope: 'write', title: t('tools.chmod-calculator.write') },
|
||||
{ scope: 'execute', title: t('tools.chmod-calculator.execute') },
|
||||
];
|
||||
const groups: Group[] = ['owner', 'group', 'public'];
|
||||
|
||||
|
@ -32,13 +33,13 @@ const symbolic = computed(() => computeChmodSymbolicRepresentation({ permissions
|
|||
<tr>
|
||||
<th class="text-center" scope="col" />
|
||||
<th class="text-center" scope="col">
|
||||
Owner (u)
|
||||
{{ t('tools.chmod-calculator.owner') }}
|
||||
</th>
|
||||
<th class="text-center" scope="col">
|
||||
Group (g)
|
||||
{{ t('tools.chmod-calculator.group') }}
|
||||
</th>
|
||||
<th class="text-center" scope="col">
|
||||
Public (o)
|
||||
{{ t('tools.chmod-calculator.public') }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { FileInvoice } from '@vicons/tabler';
|
||||
import { defineTool } from '../tool';
|
||||
import { translate as t } from '@/plugins/i18n.plugin';
|
||||
|
||||
export const tool = defineTool({
|
||||
name: 'Chmod calculator',
|
||||
name: t('tools.chmod-calculator.title'),
|
||||
path: '/chmod-calculator',
|
||||
description: 'Compute your chmod permissions and commands with this online chmod calculator.',
|
||||
description: t('tools.chmod-calculator.description'),
|
||||
keywords: [
|
||||
'chmod',
|
||||
'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">
|
||||
import cronstrue from 'cronstrue';
|
||||
import 'cronstrue/locales/zh_CN';
|
||||
import 'cronstrue/locales/fr';
|
||||
import { isValidCron } from 'cron-validator';
|
||||
import { useStyleStore } from '@/stores/style.store';
|
||||
|
||||
|
@ -9,84 +11,86 @@ function isCronValid(v: string) {
|
|||
|
||||
const styleStore = useStyleStore();
|
||||
|
||||
const { t, locale } = useI18n();
|
||||
const cron = ref('40 * * * *');
|
||||
const cronstrueConfig = reactive({
|
||||
verbose: true,
|
||||
dayOfWeekStartIndexZero: true,
|
||||
use24HourTimeFormat: true,
|
||||
throwExceptionOnParseError: true,
|
||||
locale: locale.value === 'zh' ? 'zh_CN' : locale,
|
||||
});
|
||||
|
||||
const helpers = [
|
||||
{
|
||||
symbol: '*',
|
||||
meaning: 'Any value',
|
||||
meaning: t('tools.crontab-generator.anyMeaning'),
|
||||
example: '* * * *',
|
||||
equivalent: 'Every minute',
|
||||
equivalent: t('tools.crontab-generator.anyExample'),
|
||||
},
|
||||
{
|
||||
symbol: '-',
|
||||
meaning: 'Range of values',
|
||||
meaning: t('tools.crontab-generator.rangeMeaning'),
|
||||
example: '1-10 * * *',
|
||||
equivalent: 'Minutes 1 through 10',
|
||||
equivalent: t('tools.crontab-generator.rangeExample'),
|
||||
},
|
||||
{
|
||||
symbol: ',',
|
||||
meaning: 'List of values',
|
||||
meaning: t('tools.crontab-generator.listMeaning'),
|
||||
example: '1,10 * * *',
|
||||
equivalent: 'At minutes 1 and 10',
|
||||
equivalent: t('tools.crontab-generator.listExample'),
|
||||
},
|
||||
{
|
||||
symbol: '/',
|
||||
meaning: 'Step values',
|
||||
meaning: t('tools.crontab-generator.stepMeaning'),
|
||||
example: '*/10 * * *',
|
||||
equivalent: 'Every 10 minutes',
|
||||
equivalent: t('tools.crontab-generator.stepExample'),
|
||||
},
|
||||
{
|
||||
symbol: '@yearly',
|
||||
meaning: 'Once every year at midnight of 1 January',
|
||||
meaning: t('tools.crontab-generator.yearlyMeaning'),
|
||||
example: '@yearly',
|
||||
equivalent: '0 0 1 1 *',
|
||||
},
|
||||
{
|
||||
symbol: '@annually',
|
||||
meaning: 'Same as @yearly',
|
||||
meaning: t('tools.crontab-generator.annuallyMeaning', { yearly: '@yearly' }),
|
||||
example: '@annually',
|
||||
equivalent: '0 0 1 1 *',
|
||||
},
|
||||
{
|
||||
symbol: '@monthly',
|
||||
meaning: 'Once a month at midnight on the first day',
|
||||
meaning: t('tools.crontab-generator.monthlyMeaning'),
|
||||
example: '@monthly',
|
||||
equivalent: '0 0 1 * *',
|
||||
},
|
||||
{
|
||||
symbol: '@weekly',
|
||||
meaning: 'Once a week at midnight on Sunday morning',
|
||||
meaning: t('tools.crontab-generator.weeklyMeaning'),
|
||||
example: '@weekly',
|
||||
equivalent: '0 0 * * 0',
|
||||
},
|
||||
{
|
||||
symbol: '@daily',
|
||||
meaning: 'Once a day at midnight',
|
||||
meaning: t('tools.crontab-generator.dailyMeaning'),
|
||||
example: '@daily',
|
||||
equivalent: '0 0 * * *',
|
||||
},
|
||||
{
|
||||
symbol: '@midnight',
|
||||
meaning: 'Same as @daily',
|
||||
meaning: t('tools.crontab-generator.midnightMeaning', { daily: '@daily' }),
|
||||
example: '@midnight',
|
||||
equivalent: '0 0 * * *',
|
||||
},
|
||||
{
|
||||
symbol: '@hourly',
|
||||
meaning: 'Once an hour at the beginning of the hour',
|
||||
meaning: t('tools.crontab-generator.hourlyMeaning'),
|
||||
example: '@hourly',
|
||||
equivalent: '0 * * * *',
|
||||
},
|
||||
{
|
||||
symbol: '@reboot',
|
||||
meaning: 'Run at startup',
|
||||
meaning: t('tools.crontab-generator.rebootMeaning'),
|
||||
example: '',
|
||||
equivalent: '',
|
||||
},
|
||||
|
@ -102,7 +106,7 @@ const cronString = computed(() => {
|
|||
const cronValidationRules = [
|
||||
{
|
||||
validator: (value: string) => isCronValid(value),
|
||||
message: 'This cron is invalid',
|
||||
message: t('tools.crontab-generator.invalidMessage'),
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
@ -127,13 +131,13 @@ const cronValidationRules = [
|
|||
|
||||
<div flex justify-center>
|
||||
<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-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-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-form-item>
|
||||
</n-form>
|
||||
|
@ -141,29 +145,29 @@ const cronValidationRules = [
|
|||
</c-card>
|
||||
<c-card>
|
||||
<pre>
|
||||
┌──────────── [optional] seconds (0 - 59)
|
||||
| ┌────────── minute (0 - 59)
|
||||
| | ┌──────── hour (0 - 23)
|
||||
| | | ┌────── day of month (1 - 31)
|
||||
| | | | ┌──── month (1 - 12) OR jan,feb,mar,apr ...
|
||||
| | | | | ┌── day of week (0 - 6, sunday=0) OR sun,mon ...
|
||||
┌──────────── {{ t('tools.crontab-generator.secondDesc') }}
|
||||
| ┌────────── {{ t('tools.crontab-generator.minuteDesc') }}
|
||||
| | ┌──────── {{ t('tools.crontab-generator.hourDesc') }}
|
||||
| | | ┌────── {{ t('tools.crontab-generator.dayOfMonthDesc') }}
|
||||
| | | | ┌──── {{ t('tools.crontab-generator.monthDesc') }}
|
||||
| | | | | ┌── {{ t('tools.crontab-generator.dayOfWeekDesc') }}
|
||||
| | | | | |
|
||||
* * * * * * command</pre>
|
||||
* * * * * * {{ t('tools.crontab-generator.command') }}</pre>
|
||||
|
||||
<div v-if="styleStore.isSmallScreen">
|
||||
<c-card v-for="{ symbol, meaning, example, equivalent } in helpers" :key="symbol" mb-3 important:border-none>
|
||||
<div>
|
||||
Symbol: <strong>{{ symbol }}</strong>
|
||||
{{ t('tools.crontab-generator.symbol') }} <strong>{{ symbol }}</strong>
|
||||
</div>
|
||||
<div>
|
||||
Meaning: <strong>{{ meaning }}</strong>
|
||||
{{ t('tools.crontab-generator.meaning') }} <strong>{{ meaning }}</strong>
|
||||
</div>
|
||||
<div>
|
||||
Example:
|
||||
{{ t('tools.crontab-generator.example') }}
|
||||
<strong><code>{{ example }}</code></strong>
|
||||
</div>
|
||||
<div>
|
||||
Equivalent: <strong>{{ equivalent }}</strong>
|
||||
{{ t('tools.crontab-generator.equivalent') }} <strong>{{ equivalent }}</strong>
|
||||
</div>
|
||||
</c-card>
|
||||
</div>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { Alarm } from '@vicons/tabler';
|
||||
import { defineTool } from '../tool';
|
||||
import { translate as t } from '@/plugins/i18n.plugin';
|
||||
|
||||
export const tool = defineTool({
|
||||
name: 'Crontab generator',
|
||||
name: t('tools.crontab-generator.title'),
|
||||
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: [
|
||||
'crontab',
|
||||
'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 TextareaCopyable from '@/components/TextareaCopyable.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
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',
|
||||
);
|
||||
|
@ -32,12 +33,12 @@ const { download } = useDownloadFileFromBase64({ source: dockerComposeBase64, fi
|
|||
<div>
|
||||
<c-input-text
|
||||
v-model:value="dockerRun"
|
||||
label="Your docker run command:"
|
||||
:label="t('tools.docker-run-to-docker-compose-converter.inputLabel')"
|
||||
style="font-family: monospace"
|
||||
multiline
|
||||
raw-text
|
||||
monospace
|
||||
placeholder="Your docker run command to convert..."
|
||||
:placeholder="t('tools.docker-run-to-docker-compose-converter.inputPlaceholder')"
|
||||
rows="3"
|
||||
/>
|
||||
|
||||
|
@ -47,12 +48,12 @@ const { download } = useDownloadFileFromBase64({ source: dockerComposeBase64, fi
|
|||
|
||||
<div mt-5 flex justify-center>
|
||||
<c-button :disabled="dockerCompose === ''" secondary @click="download">
|
||||
Download docker-compose.yml
|
||||
{{ t('tools.docker-run-to-docker-compose-converter.downloadBtn') }}
|
||||
</c-button>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<li v-for="(message, index) of notComposable" :key="index">
|
||||
{{ message }}
|
||||
|
@ -63,7 +64,7 @@ const { download } = useDownloadFileFromBase64({ source: dockerComposeBase64, fi
|
|||
|
||||
<div v-if="notImplemented.length > 0">
|
||||
<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"
|
||||
mt-5
|
||||
>
|
||||
|
@ -76,7 +77,7 @@ const { download } = useDownloadFileFromBase64({ source: dockerComposeBase64, fi
|
|||
</div>
|
||||
|
||||
<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>
|
||||
<li v-for="(message, index) of errors" :key="index">
|
||||
{{ message }}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { BrandDocker } from '@vicons/tabler';
|
||||
import { defineTool } from '../tool';
|
||||
import { translate as t } from '@/plugins/i18n.plugin';
|
||||
|
||||
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',
|
||||
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'],
|
||||
component: () => import('./docker-run-to-docker-compose-converter.vue'),
|
||||
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">
|
||||
import { useThemeVars } from 'naive-ui';
|
||||
import Memo from './git-memo.content.md';
|
||||
import MemoZH from './git-memo.content.zh.md';
|
||||
|
||||
const themeVars = useThemeVars();
|
||||
const { locale } = useI18n();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<Memo />
|
||||
<MemoZH v-if="locale === 'zh'" />
|
||||
<Memo v-else />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { BrandGit } from '@vicons/tabler';
|
||||
import { defineTool } from '../tool';
|
||||
import { translate as t } from '@/plugins/i18n.plugin';
|
||||
|
||||
export const tool = defineTool({
|
||||
name: 'Git cheatsheet',
|
||||
name: t('tools.git-memo.title'),
|
||||
path: '/git-memo',
|
||||
description:
|
||||
'Git is a decentralized version management software. With this cheatsheet you will have a quick access to the most common git commands.',
|
||||
description: t('tools.git-memo.description'),
|
||||
keywords: ['git', 'push', 'force', 'pull', 'commit', 'amend', 'rebase', 'merge', 'reset', 'soft', 'hard', 'lease'],
|
||||
component: () => import('./git-memo.vue'),
|
||||
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 { defineTool } from '../tool';
|
||||
import { translate as t } from '@/plugins/i18n.plugin';
|
||||
|
||||
export const tool = defineTool({
|
||||
name: 'JSON minify',
|
||||
name: t('tools.json-minify.title'),
|
||||
path: '/json-minify',
|
||||
description: 'Minify and compress your JSON by removing unnecessary white spaces.',
|
||||
description: t('tools.json-minify.description'),
|
||||
keywords: ['json', 'minify', 'format'],
|
||||
component: () => import('./json-minify.vue'),
|
||||
icon: Braces,
|
||||
|
|
|
@ -3,23 +3,24 @@ import JSON5 from 'json5';
|
|||
import type { UseValidationRule } from '@/composable/validation';
|
||||
import { withDefaultOnError } from '@/utils/defaults';
|
||||
|
||||
const { t } = useI18n();
|
||||
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 rules: UseValidationRule<string>[] = [
|
||||
{
|
||||
validator: (v: string) => v === '' || JSON5.parse(v),
|
||||
message: 'Provided JSON is not valid.',
|
||||
message: t('tools.json-minify.invalidMessage'),
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<format-transformer
|
||||
input-label="Your raw JSON"
|
||||
:input-label="t('tools.json-minify.inputLabel')"
|
||||
:input-default="defaultValue"
|
||||
input-placeholder="Paste your raw JSON here..."
|
||||
output-label="Minified version of your JSON"
|
||||
:input-placeholder="t('tools.json-minify.inputPlaceholder')"
|
||||
:output-label="t('tools.json-minify.outputLabel')"
|
||||
output-language="json"
|
||||
:input-validation-rules="rules"
|
||||
: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 { defineTool } from '../tool';
|
||||
import { translate as t } from '@/plugins/i18n.plugin';
|
||||
|
||||
export const tool = defineTool({
|
||||
name: 'JSON to CSV',
|
||||
name: t('tools.json-to-csv.title'),
|
||||
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'],
|
||||
component: () => import('./json-to-csv.vue'),
|
||||
icon: List,
|
||||
|
|
|
@ -13,19 +13,20 @@ function transformer(value: string) {
|
|||
}, '');
|
||||
}
|
||||
|
||||
const { t } = useI18n();
|
||||
const rules: UseValidationRule<string>[] = [
|
||||
{
|
||||
validator: (v: string) => v === '' || JSON5.parse(v),
|
||||
message: 'Provided JSON is not valid.',
|
||||
message: t('tools.json-to-csv.invalidMessage'),
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<format-transformer
|
||||
input-label="Your raw JSON"
|
||||
input-placeholder="Paste your raw JSON here..."
|
||||
output-label="CSV version of your JSON"
|
||||
:input-label="t('tools.json-to-csv.inputLabel')"
|
||||
:input-placeholder="t('tools.json-to-csv.inputPlaceholder')"
|
||||
:output-label="t('tools.json-to-csv.outputLabel')"
|
||||
:input-validation-rules="rules"
|
||||
: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 { defineTool } from '../tool';
|
||||
import { translate as t } from '@/plugins/i18n.plugin';
|
||||
|
||||
export const tool = defineTool({
|
||||
name: 'JSON prettify and format',
|
||||
name: t('tools.json-prettify.title'),
|
||||
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'],
|
||||
component: () => import('./json-viewer.vue'),
|
||||
icon: Braces,
|
||||
|
|
|
@ -6,6 +6,7 @@ import { withDefaultOnError } from '@/utils/defaults';
|
|||
import { useValidation } from '@/composable/validation';
|
||||
import TextareaCopyable from '@/components/TextareaCopyable.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const inputElement = ref<HTMLElement>();
|
||||
|
||||
const rawJson = useStorage('json-prettify:raw-json', '{"hello": "world", "foo": "bar"}');
|
||||
|
@ -18,7 +19,7 @@ const rawJsonValidation = useValidation({
|
|||
rules: [
|
||||
{
|
||||
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>
|
||||
<div style="flex: 0 0 100%">
|
||||
<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-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-form-item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<n-form-item
|
||||
label="Your raw JSON"
|
||||
:label="t('tools.json-prettify.rawJSONLabel')"
|
||||
:feedback="rawJsonValidation.message"
|
||||
:validation-status="rawJsonValidation.status"
|
||||
>
|
||||
<c-input-text
|
||||
ref="inputElement"
|
||||
v-model:value="rawJson"
|
||||
placeholder="Paste your raw JSON here..."
|
||||
:placeholder="t('tools.json-prettify.rawJSONPlaceholder')"
|
||||
rows="20"
|
||||
multiline
|
||||
autocomplete="off"
|
||||
|
@ -54,7 +55,7 @@ const rawJsonValidation = useValidation({
|
|||
monospace
|
||||
/>
|
||||
</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" />
|
||||
</n-form-item>
|
||||
</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 { defineTool } from '../tool';
|
||||
import { translate as t } from '@/plugins/i18n.plugin';
|
||||
|
||||
export const tool = defineTool({
|
||||
name: 'QR Code generator',
|
||||
name: t('tools.qrcode-generator.title'),
|
||||
path: '/qrcode-generator',
|
||||
description:
|
||||
'Generate and download QR-code for an url or just a text and customize the background and foreground colors.',
|
||||
description: t('tools.qrcode-generator.description'),
|
||||
keywords: ['qr', 'code', 'generator', 'square', 'color', 'link', 'low', 'medium', 'quartile', 'high', 'transparent'],
|
||||
component: () => import('./qr-code-generator.vue'),
|
||||
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 { useDownloadFileFromBase64 } from '@/composable/downloadBase64';
|
||||
|
||||
const { t } = useI18n();
|
||||
const foreground = ref('#000000ff');
|
||||
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 { qrcode } = useQRCode({
|
||||
|
@ -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.textLabel')"
|
||||
multiline
|
||||
rows="1"
|
||||
autosize
|
||||
placeholder="Your link or text..."
|
||||
:placeholder="t('tools.qrcode-generator.textPlaceholder')"
|
||||
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')">
|
||||
<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')">
|
||||
<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.errorCorrectionLevels')"
|
||||
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.downloadBtn') }}
|
||||
</c-button>
|
||||
</div>
|
||||
</n-gi>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { Server } from '@vicons/tabler';
|
||||
import { defineTool } from '../tool';
|
||||
import { translate as t } from '@/plugins/i18n.plugin';
|
||||
|
||||
export const tool = defineTool({
|
||||
name: 'Random port generator',
|
||||
name: t('tools.random-port-generator.title'),
|
||||
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'],
|
||||
component: () => import('./random-port-generator.vue'),
|
||||
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 { useCopy } from '@/composable/copy';
|
||||
|
||||
const { t } = useI18n();
|
||||
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>
|
||||
|
||||
<template>
|
||||
|
@ -15,10 +16,10 @@ const { copy } = useCopy({ source: port, text: 'Port copied to the clipboard' })
|
|||
</div>
|
||||
<div flex justify-center gap-3>
|
||||
<c-button @click="copy()">
|
||||
Copy
|
||||
{{ t('tools.random-port-generator.copyBtn') }}
|
||||
</c-button>
|
||||
<c-button @click="refreshPort">
|
||||
Refresh
|
||||
{{ t('tools.random-port-generator.refreshBtn') }}
|
||||
</c-button>
|
||||
</div>
|
||||
</c-card>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { Database } from '@vicons/tabler';
|
||||
import { defineTool } from '../tool';
|
||||
import { translate as t } from '@/plugins/i18n.plugin';
|
||||
|
||||
export const tool = defineTool({
|
||||
name: 'SQL prettify and format',
|
||||
name: t('tools.sql-prettify.title'),
|
||||
path: '/sql-prettify',
|
||||
description: 'Format and prettify your SQL queries online (it supports various SQL dialects).',
|
||||
description: t('tools.sql-prettify.description'),
|
||||
keywords: [
|
||||
'sql',
|
||||
'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 { useStyleStore } from '@/stores/style.store';
|
||||
|
||||
const { t } = useI18n();
|
||||
const inputElement = ref<HTMLElement>();
|
||||
const styleStore = useStyleStore();
|
||||
const config = reactive<FormatOptionsWithLanguage>({
|
||||
|
@ -23,7 +24,7 @@ const prettySQL = computed(() => formatSQL(rawSQL.value, config));
|
|||
<c-select
|
||||
v-model:value="config.language"
|
||||
flex-1
|
||||
label="Dialect"
|
||||
:label="t('tools.sql-prettify.dialect')"
|
||||
:options="[
|
||||
{ label: 'GCP BigQuery', value: 'bigquery' },
|
||||
{ label: 'IBM DB2', value: 'db2' },
|
||||
|
@ -35,37 +36,37 @@ const prettySQL = computed(() => formatSQL(rawSQL.value, config));
|
|||
{ label: 'PostgreSQL', value: 'postgresql' },
|
||||
{ label: 'Amazon Redshift', value: 'redshift' },
|
||||
{ label: 'Spark', value: 'spark' },
|
||||
{ label: 'Standard SQL', value: 'sql' },
|
||||
{ label: t('tools.sql-prettify.sql'), value: 'sql' },
|
||||
{ label: 'sqlite', value: 'sqlite' },
|
||||
{ label: 'SQL Server Transact-SQL', value: 'tsql' },
|
||||
]"
|
||||
/>
|
||||
<c-select
|
||||
v-model:value="config.keywordCase" label="Keyword case"
|
||||
v-model:value="config.keywordCase" :label="t('tools.sql-prettify.keywordCase')"
|
||||
flex-1
|
||||
:options="[
|
||||
{ label: 'UPPERCASE', value: 'upper' },
|
||||
{ label: 'lowercase', value: 'lower' },
|
||||
{ label: 'Preserve', value: 'preserve' },
|
||||
{ label: t('tools.sql-prettify.upper'), value: 'upper' },
|
||||
{ label: t('tools.sql-prettify.lower'), value: 'lower' },
|
||||
{ label: t('tools.sql-prettify.preserve'), value: 'preserve' },
|
||||
]"
|
||||
/>
|
||||
<c-select
|
||||
v-model:value="config.indentStyle" label="Indent style"
|
||||
v-model:value="config.indentStyle" :label="t('tools.sql-prettify.indentStyle')"
|
||||
flex-1
|
||||
:options="[
|
||||
{ label: 'Standard', value: 'standard' },
|
||||
{ label: 'Tabular left', value: 'tabularLeft' },
|
||||
{ label: 'Tabular right', value: 'tabularRight' },
|
||||
{ label: t('tools.sql-prettify.standard'), value: 'standard' },
|
||||
{ label: t('tools.sql-prettify.tabularLeft'), value: 'tabularLeft' },
|
||||
{ label: t('tools.sql-prettify.tabularRight'), value: 'tabularRight' },
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<n-form-item label="Your SQL query">
|
||||
<n-form-item :label="t('tools.sql-prettify.inputLabel')">
|
||||
<c-input-text
|
||||
ref="inputElement"
|
||||
v-model:value="rawSQL"
|
||||
placeholder="Put your SQL query here..."
|
||||
:placeholder="t('tools.sql-prettify.inputPlaceholder')"
|
||||
rows="20"
|
||||
multiline
|
||||
autocomplete="off"
|
||||
|
@ -75,7 +76,7 @@ const prettySQL = computed(() => formatSQL(rawSQL.value, config));
|
|||
monospace
|
||||
/>
|
||||
</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" />
|
||||
</n-form-item>
|
||||
</template>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { ImageOutlined } from '@vicons/material';
|
||||
import { defineTool } from '../tool';
|
||||
import { translate as t } from '@/plugins/i18n.plugin';
|
||||
|
||||
export const tool = defineTool({
|
||||
name: 'SVG placeholder generator',
|
||||
name: t('tools.svg-placeholder-generator.title'),
|
||||
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'],
|
||||
component: () => import('./svg-placeholder-generator.vue'),
|
||||
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 { textToBase64 } from '@/utils/base64';
|
||||
|
||||
const { t } = useI18n();
|
||||
const width = ref(600);
|
||||
const height = ref(350);
|
||||
const fontSize = ref(26);
|
||||
|
@ -35,57 +36,57 @@ const { download } = useDownloadFileFromBase64({ source: base64 });
|
|||
<div>
|
||||
<n-form label-placement="left" label-width="100">
|
||||
<div flex gap-3>
|
||||
<n-form-item label="Width (in px)" flex-1>
|
||||
<n-input-number v-model:value="width" placeholder="SVG width..." min="1" />
|
||||
<n-form-item :label="t('tools.svg-placeholder-generator.widthLabel')" flex-1>
|
||||
<n-input-number v-model:value="width" :placeholder="t('tools.svg-placeholder-generator.widthPlaceholder')" min="1" />
|
||||
</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-form-item>
|
||||
</div>
|
||||
<div flex gap-3>
|
||||
<n-form-item label="Height (in px)" flex-1>
|
||||
<n-input-number v-model:value="height" placeholder="SVG height..." min="1" />
|
||||
<n-form-item :label="t('tools.svg-placeholder-generator.heightLabel')" flex-1>
|
||||
<n-input-number v-model:value="height" :placeholder="t('tools.svg-placeholder-generator.heightPlaceholder')" min="1" />
|
||||
</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-form-item>
|
||||
</div>
|
||||
<div flex gap-3>
|
||||
<n-form-item label="Font size" flex-1>
|
||||
<n-input-number v-model:value="fontSize" placeholder="Font size..." min="1" />
|
||||
<n-form-item :label="t('tools.svg-placeholder-generator.fontSizeLabel')" flex-1>
|
||||
<n-input-number v-model:value="fontSize" :placeholder="t('tools.svg-placeholder-generator.fontSizePlaceholder')" min="1" />
|
||||
</n-form-item>
|
||||
|
||||
<c-input-text
|
||||
v-model:value="customText"
|
||||
label="Custom text"
|
||||
:placeholder="`Default is ${width}x${height}`"
|
||||
:label="t('tools.svg-placeholder-generator.customTextLabel')"
|
||||
:placeholder="t('tools.svg-placeholder-generator.customTextPlaceholder', { width, height })"
|
||||
label-position="left"
|
||||
label-width="100px"
|
||||
label-align="right"
|
||||
flex-1
|
||||
/>
|
||||
</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-form-item>
|
||||
</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" />
|
||||
</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" />
|
||||
</n-form-item>
|
||||
|
||||
<div flex justify-center gap-3>
|
||||
<c-button @click="copySVG()">
|
||||
Copy svg
|
||||
{{ t('tools.svg-placeholder-generator.copySvg') }}
|
||||
</c-button>
|
||||
<c-button @click="copyBase64()">
|
||||
Copy base64
|
||||
{{ t('tools.svg-placeholder-generator.copyBase64') }}
|
||||
</c-button>
|
||||
<c-button @click="download()">
|
||||
Download svg
|
||||
{{ t('tools.svg-placeholder-generator.downloadSvg') }}
|
||||
</c-button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { Qrcode } from '@vicons/tabler';
|
||||
import { defineTool } from '../tool';
|
||||
import { translate as t } from '@/plugins/i18n.plugin';
|
||||
|
||||
export const tool = defineTool({
|
||||
name: 'WiFi QR Code generator',
|
||||
name: t('tools.wifi-qrcode-generator.title'),
|
||||
path: '/wifi-qrcode-generator',
|
||||
description:
|
||||
'Generate and download QR-codes for quick connections to WiFi networks.',
|
||||
description: t('tools.wifi-qrcode-generator.description'),
|
||||
keywords: ['qr', 'code', 'generator', 'square', 'color', 'link', 'low', 'medium', 'quartile', 'high', 'transparent', 'wifi'],
|
||||
component: () => import('./wifi-qr-code-generator.vue'),
|
||||
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';
|
||||
import { useDownloadFileFromBase64 } from '@/composable/downloadBase64';
|
||||
|
||||
const { t } = useI18n();
|
||||
const foreground = ref('#000000ff');
|
||||
const background = ref('#ffffffff');
|
||||
|
||||
|
@ -42,7 +43,7 @@ const { download } = useDownloadFileFromBase64({ source: qrcode, filename: 'qr-c
|
|||
<c-select
|
||||
v-model:value="encryption"
|
||||
mb-4
|
||||
label="Encryption method"
|
||||
:label="t('tools.wifi-qrcode-generator.encryption')"
|
||||
default-value="WPA"
|
||||
label-position="left"
|
||||
label-width="130px"
|
||||
|
@ -72,14 +73,13 @@ const { download } = useDownloadFileFromBase64({ source: qrcode, filename: 'qr-c
|
|||
label-position="left"
|
||||
label-width="130px"
|
||||
label-align="right"
|
||||
label="SSID:"
|
||||
:label="t('tools.wifi-qrcode-generator.ssidLabel')"
|
||||
rows="1"
|
||||
autosize
|
||||
placeholder="Your WiFi SSID..."
|
||||
mb-6
|
||||
:placeholder="t('tools.wifi-qrcode-generator.ssidPlaceholder')"
|
||||
/>
|
||||
<n-checkbox v-model:checked="isHiddenSSID">
|
||||
Hidden SSID
|
||||
<n-checkbox v-model:checked="isHiddenSSID" w-40>
|
||||
{{ t('tools.wifi-qrcode-generator.hiddenSSID') }}
|
||||
</n-checkbox>
|
||||
</div>
|
||||
<c-input-text
|
||||
|
@ -88,17 +88,17 @@ const { download } = useDownloadFileFromBase64({ source: qrcode, filename: 'qr-c
|
|||
label-position="left"
|
||||
label-width="130px"
|
||||
label-align="right"
|
||||
label="Password:"
|
||||
:label="t('tools.wifi-qrcode-generator.passwordLabel')"
|
||||
rows="1"
|
||||
autosize
|
||||
type="password"
|
||||
placeholder="Your WiFi Password..."
|
||||
:placeholder="t('tools.wifi-qrcode-generator.passwordPlaceholder')"
|
||||
mb-6
|
||||
/>
|
||||
<c-select
|
||||
v-if="encryption === 'WPA2-EAP'"
|
||||
v-model:value="eapMethod"
|
||||
label="EAP method"
|
||||
:label="t('tools.wifi-qrcode-generator.eapMethod')"
|
||||
label-position="left"
|
||||
label-width="130px"
|
||||
label-align="right"
|
||||
|
@ -111,20 +111,19 @@ const { download } = useDownloadFileFromBase64({ source: qrcode, filename: 'qr-c
|
|||
label-position="left"
|
||||
label-width="130px"
|
||||
label-align="right"
|
||||
label="Identity:"
|
||||
:label="t('tools.wifi-qrcode-generator.eapIdentityLabel')"
|
||||
rows="1"
|
||||
autosize
|
||||
placeholder="Your EAP Identity..."
|
||||
mb-6
|
||||
:placeholder="t('tools.wifi-qrcode-generator.eapIdentityPlaceholder')"
|
||||
/>
|
||||
<n-checkbox v-model:checked="eapAnonymous">
|
||||
Anonymous?
|
||||
<n-checkbox v-model:checked="eapAnonymous" w-40>
|
||||
{{ t('tools.wifi-qrcode-generator.anonymous') }}
|
||||
</n-checkbox>
|
||||
</div>
|
||||
<c-select
|
||||
v-if="encryption === 'WPA2-EAP'"
|
||||
v-model:value="eapPhase2Method"
|
||||
label="EAP Phase 2 method"
|
||||
:label="t('tools.wifi-qrcode-generator.eapPhase2Method')"
|
||||
label-position="left"
|
||||
label-width="130px"
|
||||
label-align="right"
|
||||
|
@ -132,19 +131,19 @@ const { download } = useDownloadFileFromBase64({ source: qrcode, filename: 'qr-c
|
|||
searchable mb-4
|
||||
/>
|
||||
<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-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-form-item>
|
||||
</n-form>
|
||||
</div>
|
||||
<div v-if="qrcode">
|
||||
<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">
|
||||
Download qr-code
|
||||
{{ t('tools.wifi-qrcode-generator.downloadBtn') }}
|
||||
</c-button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { Code } from '@vicons/tabler';
|
||||
import { defineTool } from '../tool';
|
||||
import { translate as t } from '@/plugins/i18n.plugin';
|
||||
|
||||
export const tool = defineTool({
|
||||
name: 'XML formatter',
|
||||
name: t('tools.xml-formatter.title'),
|
||||
path: '/xml-formatter',
|
||||
description: 'Prettify your XML string to a human friendly readable format.',
|
||||
description: t('tools.xml-formatter.description'),
|
||||
keywords: ['xml', 'prettify', 'format'],
|
||||
component: () => import('./xml-formatter.vue'),
|
||||
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 type { UseValidationRule } from '@/composable/validation';
|
||||
|
||||
const { t } = useI18n();
|
||||
const defaultValue = '<hello><world>foo</world><world>bar</world></hello>';
|
||||
const indentSize = useStorage('xml-formatter:indent-size', 2);
|
||||
const collapseContent = useStorage('xml-formatter:collapse-content', true);
|
||||
|
@ -17,7 +18,7 @@ function transformer(value: string) {
|
|||
const rules: UseValidationRule<string>[] = [
|
||||
{
|
||||
validator: isValidXML,
|
||||
message: 'Provided XML is not valid.',
|
||||
message: t('tools.xml-formatter.invalidMessage'),
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
@ -25,19 +26,19 @@ const rules: UseValidationRule<string>[] = [
|
|||
<template>
|
||||
<div important:flex-full important:flex-shrink-0 important:flex-grow-0>
|
||||
<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-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-form-item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<format-transformer
|
||||
input-label="Your XML"
|
||||
input-placeholder="Paste your XML here..."
|
||||
output-label="Formatted XML from your XML"
|
||||
:input-label="t('tools.xml-formatter.inputLabel')"
|
||||
:input-placeholder="t('tools.xml-formatter.inputPlaceholder')"
|
||||
:output-label="t('tools.xml-formatter.outputLabel')"
|
||||
output-language="xml"
|
||||
:input-validation-rules="rules"
|
||||
:transformer="transformer"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue