WIP(translate): translate two category all tools
This commit is contained in:
		
							parent
							
								
									9db4b41daf
								
							
						
					
					
						commit
						4f550a9499
					
				
							
								
								
									
										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…
	
		Reference in New Issue
	
	Block a user