feat(translate): Complete translation of all tools
This commit is contained in:
		
							parent
							
								
									a2e498d0aa
								
							
						
					
					
						commit
						ae71454a38
					
				| @ -7,6 +7,7 @@ import { arrayToMarkdownTable, computeAverage, computeVariance } from './benchma | ||||
| import DynamicValues from './dynamic-values.vue'; | ||||
| import { useCopy } from '@/composable/copy'; | ||||
| 
 | ||||
| const { t } = useI18n(); | ||||
| const suites = useStorage('benchmark-builder:suites', [ | ||||
|   { title: 'Suite 1', data: [5, 10] }, | ||||
|   { title: 'Suite 2', data: [8, 12] }, | ||||
| @ -51,11 +52,11 @@ const results = computed(() => { | ||||
| const { copy } = useCopy({ createToast: false }); | ||||
| 
 | ||||
| const header = { | ||||
|   position: 'Position', | ||||
|   title: 'Suite', | ||||
|   size: 'Samples', | ||||
|   mean: 'Mean', | ||||
|   variance: 'Variance', | ||||
|   position: t('tools.benchmark-builder.header.position'), | ||||
|   title: t('tools.benchmark-builder.header.title'), | ||||
|   size: t('tools.benchmark-builder.header.size'), | ||||
|   mean: t('tools.benchmark-builder.header.mean'), | ||||
|   variance: t('tools.benchmark-builder.header.variance'), | ||||
| }; | ||||
| 
 | ||||
| function copyAsMarkdown() { | ||||
| @ -86,13 +87,13 @@ function copyAsBulletList() { | ||||
|           <c-input-text | ||||
|             v-model:value="suite.title" | ||||
|             label-position="left" | ||||
|             label="Suite name" | ||||
|             placeholder="Suite name..." | ||||
|             :label="t('tools.benchmark-builder.suiteNameLabel')" | ||||
|             :placeholder="t('tools.benchmark-builder.suiteNamePlaceholder')" | ||||
|             clearable | ||||
|           /> | ||||
| 
 | ||||
|           <n-divider /> | ||||
|           <n-form-item label="Suite values" :show-feedback="false"> | ||||
|           <n-form-item :label="t('tools.benchmark-builder.suiteValues')" :show-feedback="false"> | ||||
|             <DynamicValues v-model:values="suite.data" /> | ||||
|           </n-form-item> | ||||
|         </c-card> | ||||
| @ -100,14 +101,14 @@ function copyAsBulletList() { | ||||
|         <div flex justify-center> | ||||
|           <c-button v-if="suites.length > 1" variant="text" @click="suites.splice(index, 1)"> | ||||
|             <n-icon :component="Trash" depth="3" mr-2 size="18" /> | ||||
|             Delete suite | ||||
|             {{ t('tools.benchmark-builder.deleteSuite') }} | ||||
|           </c-button> | ||||
|           <c-button | ||||
|             variant="text" | ||||
|             @click="suites.splice(index + 1, 0, { data: [0], title: `Suite ${suites.length + 1}` })" | ||||
|           > | ||||
|             <n-icon :component="Plus" depth="3" mr-2 size="18" /> | ||||
|             Add suite | ||||
|             {{ t('tools.benchmark-builder.addSuite') }} | ||||
|           </c-button> | ||||
|         </div> | ||||
|       </div> | ||||
| @ -117,7 +118,7 @@ function copyAsBulletList() { | ||||
|   <div style="flex: 0 0 100%"> | ||||
|     <div style="max-width: 600px; margin: 0 auto"> | ||||
|       <div mx-auto max-w-sm flex justify-center gap-3> | ||||
|         <c-input-text v-model:value="unit" placeholder="Unit (eg: ms)" label="Unit" label-position="left" mb-4 /> | ||||
|         <c-input-text v-model:value="unit" :placeholder="t('tools.benchmark-builder.unitPlaceholder')" :label="t('tools.benchmark-builder.unitLabel')" label-position="left" mb-4 /> | ||||
| 
 | ||||
|         <c-button | ||||
|           @click=" | ||||
| @ -127,7 +128,7 @@ function copyAsBulletList() { | ||||
|             ] | ||||
|           " | ||||
|         > | ||||
|           Reset suites | ||||
|           {{ t('tools.benchmark-builder.resetSuites') }} | ||||
|         </c-button> | ||||
|       </div> | ||||
| 
 | ||||
| @ -135,10 +136,10 @@ function copyAsBulletList() { | ||||
| 
 | ||||
|       <div mt-5 flex justify-center gap-3> | ||||
|         <c-button @click="copyAsMarkdown()"> | ||||
|           Copy as markdown table | ||||
|           {{ t('tools.benchmark-builder.copyAsMarkdown') }} | ||||
|         </c-button> | ||||
|         <c-button @click="copyAsBulletList()"> | ||||
|           Copy as bullet list | ||||
|           {{ t('tools.benchmark-builder.copyAsBulletList') }} | ||||
|         </c-button> | ||||
|       </div> | ||||
|     </div> | ||||
|  | ||||
| @ -10,6 +10,7 @@ const emit = defineEmits(['update:values']); | ||||
| 
 | ||||
| const refs = useTemplateRefsList<typeof NInputNumber>(); | ||||
| 
 | ||||
| const { t } = useI18n(); | ||||
| const values = useVModel(props, 'values', emit); | ||||
| 
 | ||||
| async function addValue() { | ||||
| @ -35,11 +36,11 @@ function onInputEnter(index: number) { | ||||
|         :ref="refs.set" | ||||
|         v-model:value="values[index]" | ||||
|         :show-button="false" | ||||
|         placeholder="Set your measure..." | ||||
|         :placeholder="t('tools.benchmark-builder.setYourMeasure')" | ||||
|         autofocus | ||||
|         @keydown.enter="onInputEnter(index)" | ||||
|       /> | ||||
|       <c-tooltip tooltip="Delete this value"> | ||||
|       <c-tooltip :tooltip="t('tools.benchmark-builder.deleteThisValue')"> | ||||
|         <c-button circle variant="text" @click="values.splice(index, 1)"> | ||||
|           <n-icon :component="Trash" depth="3" size="18" /> | ||||
|         </c-button> | ||||
| @ -48,7 +49,7 @@ function onInputEnter(index: number) { | ||||
| 
 | ||||
|     <c-button @click="addValue"> | ||||
|       <n-icon :component="Plus" depth="3" mr-2 size="18" /> | ||||
|       Add a measure | ||||
|       {{ t('tools.benchmark-builder.addAMeasure') }} | ||||
|     </c-button> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| @ -1,10 +1,11 @@ | ||||
| import { SpeedFilled } from '@vicons/material'; | ||||
| import { defineTool } from '../tool'; | ||||
| import { translate as t } from '@/plugins/i18n.plugin'; | ||||
| 
 | ||||
| export const tool = defineTool({ | ||||
|   name: 'Benchmark builder', | ||||
|   name: t('tools.benchmark-builder.title'), | ||||
|   path: '/benchmark-builder', | ||||
|   description: 'Easily compare execution time of tasks with this very simple online benchmark builder.', | ||||
|   description: t('tools.benchmark-builder.description'), | ||||
|   keywords: ['benchmark', 'builder', 'execution', 'duration', 'mean', 'variance'], | ||||
|   component: () => import('./benchmark-builder.vue'), | ||||
|   icon: SpeedFilled, | ||||
|  | ||||
							
								
								
									
										26
									
								
								src/tools/benchmark-builder/locales/en.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/tools/benchmark-builder/locales/en.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| tools: | ||||
|   benchmark-builder: | ||||
|     title: Benchmark builder | ||||
|     description: Easily compare execution time of tasks with this very simple online benchmark builder. | ||||
| 
 | ||||
|     header: | ||||
|       position: Position | ||||
|       title: Suite | ||||
|       size: Samples | ||||
|       mean: Mean | ||||
|       variance: Variance | ||||
|     suiteNameLabel: Suite name | ||||
|     suiteNamePlaceholder: Suite name... | ||||
|     suiteValues: Suite values | ||||
|     suiteTitle: Suite {index} | ||||
|     deleteSuite: Delete suite | ||||
|     addSuite: Add suite | ||||
|     resetSuites: Reset suites | ||||
|     unitLabel: Unit | ||||
|     unitPlaceholder: 'Unit (eg: ms)' | ||||
|     setYourMeasure: Set your measure... | ||||
|     deleteThisValue: Delete this value | ||||
|     addAMeasure: Add a measure | ||||
| 
 | ||||
|     copyAsMarkdown: Copy as markdown table | ||||
|     copyAsBulletList: Copy as bullet list | ||||
							
								
								
									
										26
									
								
								src/tools/benchmark-builder/locales/zh.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/tools/benchmark-builder/locales/zh.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| tools: | ||||
|   benchmark-builder: | ||||
|     title: 基准测试生成器 | ||||
|     description: 使用这个非常简单的在线基准测试生成器轻松比较任务的执行时间。 | ||||
| 
 | ||||
|     header: | ||||
|       position: 位置 | ||||
|       title: 套件 | ||||
|       size: 样本数 | ||||
|       mean: 平均值 | ||||
|       variance: 方差 | ||||
|     suiteNameLabel: 套件名称 | ||||
|     suiteNamePlaceholder: 输入套件名称... | ||||
|     suiteValues: 套件数值 | ||||
|     suiteTitle: 套件 {index} | ||||
|     deleteSuite: 删除套件 | ||||
|     addSuite: 添加套件 | ||||
|     resetSuites: 重置套件 | ||||
|     unitLabel: 单位 | ||||
|     unitPlaceholder: '单位(例如:毫秒)' | ||||
|     setYourMeasure: 设置您的度量... | ||||
|     deleteThisValue: 删除此数值 | ||||
|     addAMeasure: 添加一个度量 | ||||
| 
 | ||||
|     copyAsMarkdown: 复制为 markdown 表格 | ||||
|     copyAsBulletList: 复制为项目列表 | ||||
| @ -3,6 +3,7 @@ import { useRafFn } from '@vueuse/core'; | ||||
| 
 | ||||
| import { formatMs } from './chronometer.service'; | ||||
| 
 | ||||
| const { t } = useI18n(); | ||||
| const isRunning = ref(false); | ||||
| const counter = ref(0); | ||||
| 
 | ||||
| @ -37,14 +38,14 @@ function pause() { | ||||
|     </c-card> | ||||
|     <div mt-5 flex justify-center gap-3> | ||||
|       <c-button v-if="!isRunning" type="primary" @click="resume"> | ||||
|         Start | ||||
|         {{ t('tools.chronometer.start') }} | ||||
|       </c-button> | ||||
|       <c-button v-else type="warning" @click="pause"> | ||||
|         Stop | ||||
|         {{ t('tools.chronometer.stop') }} | ||||
|       </c-button> | ||||
| 
 | ||||
|       <c-button @click="counter = 0"> | ||||
|         Reset | ||||
|         {{ t('tools.chronometer.reset') }} | ||||
|       </c-button> | ||||
|     </div> | ||||
|   </div> | ||||
|  | ||||
| @ -1,10 +1,11 @@ | ||||
| import { TimerOutlined } from '@vicons/material'; | ||||
| import { defineTool } from '../tool'; | ||||
| import { translate as t } from '@/plugins/i18n.plugin'; | ||||
| 
 | ||||
| export const tool = defineTool({ | ||||
|   name: 'Chronometer', | ||||
|   name: t('tools.chronometer.title'), | ||||
|   path: '/chronometer', | ||||
|   description: 'Monitor the duration of a thing. Basically a chronometer with simple chronometer features.', | ||||
|   description: t('tools.chronometer.description'), | ||||
|   keywords: ['chronometer', 'time', 'lap', 'duration', 'measure', 'pause', 'resume', 'stopwatch'], | ||||
|   component: () => import('./chronometer.vue'), | ||||
|   icon: TimerOutlined, | ||||
|  | ||||
							
								
								
									
										8
									
								
								src/tools/chronometer/locales/en.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/tools/chronometer/locales/en.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| tools: | ||||
|   chronometer: | ||||
|     title: Chronometer | ||||
|     description: Monitor the duration of a thing. Basically a chronometer with simple chronometer features. | ||||
| 
 | ||||
|     start: Start | ||||
|     stop: Stop | ||||
|     reset: Reset | ||||
							
								
								
									
										8
									
								
								src/tools/chronometer/locales/zh.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/tools/chronometer/locales/zh.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| tools: | ||||
|   chronometer: | ||||
|     title: 计时器 | ||||
|     description: 监控事物的持续时间。基本上是一个具有简单计时器功能的计时器。 | ||||
| 
 | ||||
|     start: 开始 | ||||
|     stop: 停止 | ||||
|     reset: 重置 | ||||
| @ -5,12 +5,13 @@ import { useCopy } from '@/composable/copy'; | ||||
| const props = (defineProps<{ emojiInfo: EmojiInfo }>()); | ||||
| const { emojiInfo } = toRefs(props); | ||||
| 
 | ||||
| const { t } = useI18n(); | ||||
| const { copy } = useCopy(); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <c-card flex items-center gap-3 important:py-8px important:pl-10px important:pr-5px> | ||||
|     <div cursor-pointer text-30px @click="copy(emojiInfo.emoji, { notificationMessage: `Emoji ${emojiInfo.emoji} copied to the clipboard` })"> | ||||
|     <div cursor-pointer text-30px @click="copy(emojiInfo.emoji, { notificationMessage: t('tools.emoji-picker.emojiCopied', { emoji: emojiInfo.emoji }) })"> | ||||
|       {{ emojiInfo.emoji }} | ||||
|     </div> | ||||
| 
 | ||||
| @ -30,10 +31,10 @@ const { copy } = useCopy(); | ||||
|       </div> --> | ||||
| 
 | ||||
|       <div flex gap-2 text-xs font-mono op-70> | ||||
|         <span cursor-pointer transition hover:text-primary @click="copy(emojiInfo.codePoints, { notificationMessage: `Code points '${emojiInfo.codePoints}' copied to the clipboard` })"> | ||||
|         <span cursor-pointer transition hover:text-primary @click="copy(emojiInfo.codePoints, { notificationMessage: t('tools.emoji-picker.codePointsCopied', { codePoints: emojiInfo.codePoints }) })"> | ||||
|           {{ emojiInfo.codePoints }} | ||||
|         </span> | ||||
|         <span cursor-pointer truncate transition hover:text-primary @click="copy(emojiInfo.unicode, { notificationMessage: `Unicode '${emojiInfo.unicode}' copied to the clipboard` })"> | ||||
|         <span cursor-pointer truncate transition hover:text-primary @click="copy(emojiInfo.unicode, { notificationMessage: t('tools.emoji-picker.unicodeCopied', { unicode: emojiInfo.unicode }) })"> | ||||
|           {{ emojiInfo.unicode }} | ||||
|         </span> | ||||
|       </div> | ||||
|  | ||||
| @ -5,6 +5,7 @@ import _ from 'lodash'; | ||||
| import type { EmojiInfo } from './emoji.types'; | ||||
| import { useFuzzySearch } from '@/composable/fuzzySearch'; | ||||
| 
 | ||||
| const { t } = useI18n(); | ||||
| const escapeUnicode = ({ emoji }: { emoji: string }) => emoji.split('').map(unit => `\\u${unit.charCodeAt(0).toString(16).padStart(4, '0')}`).join(''); | ||||
| const getEmojiCodePoints = ({ emoji }: { emoji: string }) => emoji.codePointAt(0) ? `0x${emoji.codePointAt(0)?.toString(16)}` : undefined; | ||||
| 
 | ||||
| @ -42,7 +43,7 @@ const { searchResult } = useFuzzySearch({ | ||||
|     <div flex items-center gap-3> | ||||
|       <c-input-text | ||||
|         v-model:value="searchQuery" | ||||
|         placeholder="Search emojis (e.g. 'smile')..." | ||||
|         :placeholder="t('tools.emoji-picker.searchPlaceholder')" | ||||
|         mx-auto max-w-600px | ||||
|       > | ||||
|         <template #prefix> | ||||
| @ -58,12 +59,12 @@ const { searchResult } = useFuzzySearch({ | ||||
|         text-20px | ||||
|         font-bold | ||||
|       > | ||||
|         No results | ||||
|         {{ t('tools.emoji-picker.noResults') }} | ||||
|       </div> | ||||
| 
 | ||||
|       <div v-else> | ||||
|         <div mt-4 text-20px font-bold> | ||||
|           Search result | ||||
|           {{ t('tools.emoji-picker.searchResult') }} | ||||
|         </div> | ||||
| 
 | ||||
|         <emoji-grid :emoji-infos="searchResult" /> | ||||
|  | ||||
| @ -1,10 +1,11 @@ | ||||
| import { MoodSmile } from '@vicons/tabler'; | ||||
| import { defineTool } from '../tool'; | ||||
| import { translate as t } from '@/plugins/i18n.plugin'; | ||||
| 
 | ||||
| export const tool = defineTool({ | ||||
|   name: 'Emoji picker', | ||||
|   name: t('tools.emoji-picker.title'), | ||||
|   path: '/emoji-picker', | ||||
|   description: 'Copy and paste emojis easily and get the unicode and code points value of each emoji.', | ||||
|   description: t('tools.emoji-picker.description'), | ||||
|   keywords: ['emoji', 'picker', 'unicode', 'copy', 'paste'], | ||||
|   component: () => import('./emoji-picker.vue'), | ||||
|   icon: MoodSmile, | ||||
|  | ||||
							
								
								
									
										12
									
								
								src/tools/emoji-picker/locales/en.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/tools/emoji-picker/locales/en.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| tools: | ||||
|   emoji-picker: | ||||
|     title: Emoji picker | ||||
|     description: Copy and paste emojis easily and get the unicode and code points value of each emoji. | ||||
| 
 | ||||
|     searchPlaceholder: Search emojis (e.g. "smile")... | ||||
|     noResults: No results | ||||
|     searchResult: Search result | ||||
| 
 | ||||
|     emojiCopied: Emoji {emoji} copied to the clipboard | ||||
|     codePointsCopied: Code points {codePoints} copied to the clipboard | ||||
|     unicodeCopied: Unicode {unicode} copied to the clipboard | ||||
							
								
								
									
										12
									
								
								src/tools/emoji-picker/locales/zh.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/tools/emoji-picker/locales/zh.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| tools: | ||||
|   emoji-picker: | ||||
|     title: 表情选择器 | ||||
|     description: 轻松复制和粘贴表情符号,并获取每个表情符号的 Unicode 和代码点值。 | ||||
| 
 | ||||
|     searchPlaceholder: 搜索表情符号(例如“笑脸”)... | ||||
|     noResults: 没有结果 | ||||
|     searchResult: 搜索结果 | ||||
| 
 | ||||
|     emojiCopied: 表情符号 {emoji} 已复制到剪贴板 | ||||
|     codePointsCopied: 代码点 {codePoints} 已复制到剪贴板 | ||||
|     unicodeCopied: Unicode {unicode} 已复制到剪贴板 | ||||
| @ -1,16 +1,17 @@ | ||||
| import { ValidationErrorsIBAN } from 'ibantools'; | ||||
| import { translate as t } from '@/plugins/i18n.plugin'; | ||||
| 
 | ||||
| export { getFriendlyErrors }; | ||||
| 
 | ||||
| const ibanErrorToMessage = { | ||||
|   [ValidationErrorsIBAN.NoIBANProvided]: 'No IBAN provided', | ||||
|   [ValidationErrorsIBAN.NoIBANCountry]: 'No IBAN country', | ||||
|   [ValidationErrorsIBAN.WrongBBANLength]: 'Wrong BBAN length', | ||||
|   [ValidationErrorsIBAN.WrongBBANFormat]: 'Wrong BBAN format', | ||||
|   [ValidationErrorsIBAN.ChecksumNotNumber]: 'Checksum is not a number', | ||||
|   [ValidationErrorsIBAN.WrongIBANChecksum]: 'Wrong IBAN checksum', | ||||
|   [ValidationErrorsIBAN.WrongAccountBankBranchChecksum]: 'Wrong account bank branch checksum', | ||||
|   [ValidationErrorsIBAN.QRIBANNotAllowed]: 'QR-IBAN not allowed', | ||||
|   [ValidationErrorsIBAN.NoIBANProvided]: t('tools.iban-validator-and-parser.noIBANProvided'), | ||||
|   [ValidationErrorsIBAN.NoIBANCountry]: t('tools.iban-validator-and-parser.noIBANCountry'), | ||||
|   [ValidationErrorsIBAN.WrongBBANLength]: t('tools.iban-validator-and-parser.wrongBBANLength'), | ||||
|   [ValidationErrorsIBAN.WrongBBANFormat]: t('tools.iban-validator-and-parser.wrongBBANFormat'), | ||||
|   [ValidationErrorsIBAN.ChecksumNotNumber]: t('tools.iban-validator-and-parser.checksumNotNumber'), | ||||
|   [ValidationErrorsIBAN.WrongIBANChecksum]: t('tools.iban-validator-and-parser.wrongIBANChecksum'), | ||||
|   [ValidationErrorsIBAN.WrongAccountBankBranchChecksum]: t('tools.iban-validator-and-parser.wrongAccountBankBranchChecksum'), | ||||
|   [ValidationErrorsIBAN.QRIBANNotAllowed]: t('tools.iban-validator-and-parser.QRIBANNotAllowed'), | ||||
| }; | ||||
| 
 | ||||
| function getFriendlyErrors(errorCodes: ValidationErrorsIBAN[]) { | ||||
|  | ||||
| @ -3,6 +3,7 @@ import { extractIBAN, friendlyFormatIBAN, isQRIBAN, validateIBAN } from 'ibantoo | ||||
| import { getFriendlyErrors } from './iban-validator-and-parser.service'; | ||||
| import type { CKeyValueListItems } from '@/ui/c-key-value-list/c-key-value-list.types'; | ||||
| 
 | ||||
| const { t } = useI18n(); | ||||
| const rawIban = ref(''); | ||||
| 
 | ||||
| const ibanInfo = computed<CKeyValueListItems>(() => { | ||||
| @ -19,31 +20,31 @@ const ibanInfo = computed<CKeyValueListItems>(() => { | ||||
|   return [ | ||||
| 
 | ||||
|     { | ||||
|       label: 'Is IBAN valid ?', | ||||
|       label: t('tools.iban-validator-and-parser.isIbanValid'), | ||||
|       value: isIbanValid, | ||||
|       showCopyButton: false, | ||||
|     }, | ||||
|     { | ||||
|       label: 'IBAN errors', | ||||
|       label: t('tools.iban-validator-and-parser.IBANErrors'), | ||||
|       value: errors.length === 0 ? undefined : errors, | ||||
|       hideOnNil: true, | ||||
|       showCopyButton: false, | ||||
|     }, | ||||
|     { | ||||
|       label: 'Is IBAN a QR-IBAN ?', | ||||
|       label: t('tools.iban-validator-and-parser.isQRIBAN'), | ||||
|       value: isQRIBAN(iban), | ||||
|       showCopyButton: false, | ||||
|     }, | ||||
|     { | ||||
|       label: 'Country code', | ||||
|       label: t('tools.iban-validator-and-parser.countryCode'), | ||||
|       value: countryCode, | ||||
|     }, | ||||
|     { | ||||
|       label: 'BBAN', | ||||
|       label: t('tools.iban-validator-and-parser.BBAN'), | ||||
|       value: bban, | ||||
|     }, | ||||
|     { | ||||
|       label: 'IBAN friendly format', | ||||
|       label: t('tools.iban-validator-and-parser.IBANFriendlyFormat'), | ||||
|       value: friendlyFormatIBAN(iban), | ||||
|     }, | ||||
|   ]; | ||||
| @ -58,13 +59,13 @@ const ibanExamples = [ | ||||
| 
 | ||||
| <template> | ||||
|   <div> | ||||
|     <c-input-text v-model:value="rawIban" placeholder="Enter an IBAN to check for validity..." test-id="iban-input" /> | ||||
|     <c-input-text v-model:value="rawIban" :placeholder="t('tools.iban-validator-and-parser.inputPlaceholder')" test-id="iban-input" /> | ||||
| 
 | ||||
|     <c-card v-if="ibanInfo.length > 0" mt-5> | ||||
|       <c-key-value-list :items="ibanInfo" data-test-id="iban-info" /> | ||||
|     </c-card> | ||||
| 
 | ||||
|     <c-card title="Valid IBAN examples" mt-5> | ||||
|     <c-card :title="t('tools.iban-validator-and-parser.ibanExamples')" mt-5> | ||||
|       <div v-for="iban in ibanExamples" :key="iban"> | ||||
|         <c-text-copyable :value="iban" font-mono :displayed-value="friendlyFormatIBAN(iban)" /> | ||||
|       </div> | ||||
|  | ||||
| @ -1,10 +1,11 @@ | ||||
| import { defineTool } from '../tool'; | ||||
| import Bank from '~icons/mdi/bank'; | ||||
| import { translate as t } from '@/plugins/i18n.plugin'; | ||||
| 
 | ||||
| export const tool = defineTool({ | ||||
|   name: 'IBAN validator and parser', | ||||
|   name: t('tools.iban-validator-and-parser.title'), | ||||
|   path: '/iban-validator-and-parser', | ||||
|   description: 'Validate and parse IBAN numbers. Check if IBAN is valid and get the country, BBAN, if it is a QR-IBAN and the IBAN friendly format.', | ||||
|   description: t('tools.iban-validator-and-parser.description'), | ||||
|   keywords: ['iban', 'validator', 'and', 'parser', 'bic', 'bank'], | ||||
|   component: () => import('./iban-validator-and-parser.vue'), | ||||
|   icon: Bank, | ||||
|  | ||||
							
								
								
									
										22
									
								
								src/tools/iban-validator-and-parser/locales/en.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/tools/iban-validator-and-parser/locales/en.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| tools: | ||||
|   iban-validator-and-parser: | ||||
|     title: IBAN validator and parser | ||||
|     description: Validate and parse IBAN numbers. Check if IBAN is valid and get the country, BBAN, if it is a QR-IBAN and the IBAN friendly format. | ||||
| 
 | ||||
|     inputPlaceholder: Enter an IBAN to check for validity... | ||||
|     ibanExamples: Valid IBAN examples | ||||
| 
 | ||||
|     isIbanValid: Is IBAN valid ? | ||||
|     IBANErrors: IBAN errors | ||||
|     isQRIBAN: Is IBAN a QR-IBAN ? | ||||
|     countryCode: Country code | ||||
|     BBAN: BBAN | ||||
|     IBANFriendlyFormat: IBAN friendly format | ||||
|     noIBANProvided: No IBAN provided | ||||
|     noIBANCountry: No IBAN country | ||||
|     wrongBBANLength: Wrong BBAN length | ||||
|     wrongBBANFormat: Wrong BBAN format | ||||
|     checksumNotNumber: Checksum is not a number | ||||
|     wrongIBANChecksum: Wrong IBAN checksum | ||||
|     wrongAccountBankBranchChecksum: Wrong account bank branch checksum | ||||
|     QRIBANNotAllowed: QR-IBAN not allowed | ||||
							
								
								
									
										22
									
								
								src/tools/iban-validator-and-parser/locales/zh.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/tools/iban-validator-and-parser/locales/zh.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| tools: | ||||
|   iban-validator-and-parser: | ||||
|     title: IBAN 验证器和解析器 | ||||
|     description: 验证和解析 IBAN 号码。检查 IBAN 是否有效,并获取国家、BBAN、是否为 QR-IBAN 和 IBAN 友好格式。 | ||||
| 
 | ||||
|     inputPlaceholder: 输入要检查有效性的 IBAN… | ||||
|     ibanExamples: 有效的 IBAN 示例 | ||||
| 
 | ||||
|     isIbanValid: IBAN 是否有效? | ||||
|     IBANErrors: IBAN 错误 | ||||
|     isQRIBAN: 是否为 QR-IBAN? | ||||
|     countryCode: 国家代码 | ||||
|     BBAN: BBAN | ||||
|     IBANFriendlyFormat: IBAN 友好格式 | ||||
|     noIBANProvided: 未提供 IBAN | ||||
|     noIBANCountry: 未提供 IBAN 国家 | ||||
|     wrongBBANLength: BBAN 长度错误 | ||||
|     wrongBBANFormat: BBAN 格式错误 | ||||
|     checksumNotNumber: 校验和不是数字 | ||||
|     wrongIBANChecksum: IBAN 校验和错误 | ||||
|     wrongAccountBankBranchChecksum: 帐户银行分行校验和错误 | ||||
|     QRIBANNotAllowed: 不允许 QR-IBAN | ||||
| @ -1,11 +1,11 @@ | ||||
| import { AlignJustified } from '@vicons/tabler'; | ||||
| import { defineTool } from '../tool'; | ||||
| import { translate as t } from '@/plugins/i18n.plugin'; | ||||
| 
 | ||||
| export const tool = defineTool({ | ||||
|   name: 'Lorem ipsum generator', | ||||
|   name: t('tools.lorem-ipsum-generator.title'), | ||||
|   path: '/lorem-ipsum-generator', | ||||
|   description: | ||||
|     'Lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document or a typeface without relying on meaningful content', | ||||
|   description: t('tools.lorem-ipsum-generator.description'), | ||||
|   keywords: ['lorem', 'ipsum', 'dolor', 'sit', 'amet', 'placeholder', 'text', 'filler', 'random', 'generator'], | ||||
|   component: () => import('./lorem-ipsum-generator.vue'), | ||||
|   icon: AlignJustified, | ||||
|  | ||||
							
								
								
									
										14
									
								
								src/tools/lorem-ipsum-generator/locales/en.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/tools/lorem-ipsum-generator/locales/en.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| tools: | ||||
|   lorem-ipsum-generator: | ||||
|     title: Lorem ipsum generator | ||||
|     description: Lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document or a typeface without relying on meaningful content. | ||||
| 
 | ||||
|     paragraphs: Paragraphs | ||||
|     sentences: Sentences per paragraph | ||||
|     words: Words per sentence | ||||
|     startWithLoremIpsum: Start with lorem ipsum ? | ||||
|     asHTML: As html ? | ||||
|     loremIpsumText: Your lorem ipsum... | ||||
| 
 | ||||
|     copyBtn: Copy | ||||
|     copied: Lorem ipsum copied to the clipboard | ||||
							
								
								
									
										14
									
								
								src/tools/lorem-ipsum-generator/locales/zh.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/tools/lorem-ipsum-generator/locales/zh.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| tools: | ||||
|   lorem-ipsum-generator: | ||||
|     title: Lorem Ipsum 生成器 | ||||
|     description: Lorem Ipsum 是一种常用的占位文本,用于展示文档或字体的视觉形式,而不依赖于有意义的内容。 | ||||
| 
 | ||||
|     paragraphs: 段落 | ||||
|     sentences: 每段句子数 | ||||
|     words: 每句字数 | ||||
|     startWithLoremIpsum: 以 Lorem Ipsum 开头? | ||||
|     asHTML: 作为 HTML? | ||||
|     loremIpsumText: 您的 Lorem Ipsum... | ||||
| 
 | ||||
|     copyBtn: 复制 | ||||
|     copied: Lorem Ipsum 已复制到剪贴板 | ||||
| @ -3,6 +3,7 @@ import { generateLoremIpsum } from './lorem-ipsum-generator.service'; | ||||
| import { useCopy } from '@/composable/copy'; | ||||
| import { randIntFromInterval } from '@/utils/random'; | ||||
| 
 | ||||
| const { t } = useI18n(); | ||||
| const paragraphs = ref(1); | ||||
| const sentences = ref([3, 8]); | ||||
| const words = ref([8, 15]); | ||||
| @ -18,32 +19,32 @@ const loremIpsumText = computed(() => | ||||
|     startWithLoremIpsum: startWithLoremIpsum.value, | ||||
|   }), | ||||
| ); | ||||
| const { copy } = useCopy({ source: loremIpsumText, text: 'Lorem ipsum copied to the clipboard' }); | ||||
| const { copy } = useCopy({ source: loremIpsumText, text: t('tools.lorem-ipsum-generator.copied') }); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <c-card> | ||||
|     <n-form-item label="Paragraphs" :show-feedback="false" label-width="200" label-placement="left"> | ||||
|     <n-form-item :label="t('tools.lorem-ipsum-generator.paragraphs')" :show-feedback="false" label-width="200" label-placement="left"> | ||||
|       <n-slider v-model:value="paragraphs" :step="1" :min="1" :max="20" /> | ||||
|     </n-form-item> | ||||
|     <n-form-item label="Sentences per paragraph" :show-feedback="false" label-width="200" label-placement="left"> | ||||
|     <n-form-item :label="t('tools.lorem-ipsum-generator.sentences')" :show-feedback="false" label-width="200" label-placement="left"> | ||||
|       <n-slider v-model:value="sentences" range :step="1" :min="1" :max="50" /> | ||||
|     </n-form-item> | ||||
|     <n-form-item label="Words per sentence" :show-feedback="false" label-width="200" label-placement="left"> | ||||
|     <n-form-item :label="t('tools.lorem-ipsum-generator.words')" :show-feedback="false" label-width="200" label-placement="left"> | ||||
|       <n-slider v-model:value="words" range :step="1" :min="1" :max="50" /> | ||||
|     </n-form-item> | ||||
|     <n-form-item label="Start with lorem ipsum ?" :show-feedback="false" label-width="200" label-placement="left"> | ||||
|     <n-form-item :label="t('tools.lorem-ipsum-generator.startWithLoremIpsum')" :show-feedback="false" label-width="200" label-placement="left"> | ||||
|       <n-switch v-model:value="startWithLoremIpsum" /> | ||||
|     </n-form-item> | ||||
|     <n-form-item label="As html ?" :show-feedback="false" label-width="200" label-placement="left"> | ||||
|     <n-form-item :label="t('tools.lorem-ipsum-generator.asHTML')" :show-feedback="false" label-width="200" label-placement="left"> | ||||
|       <n-switch v-model:value="asHTML" /> | ||||
|     </n-form-item> | ||||
| 
 | ||||
|     <c-input-text :value="loremIpsumText" multiline placeholder="Your lorem ipsum..." readonly mt-5 rows="5" /> | ||||
|     <c-input-text :value="loremIpsumText" multiline :placeholder="t('tools.lorem-ipsum-generator.loremIpsumText')" readonly mt-5 rows="5" /> | ||||
| 
 | ||||
|     <div mt-5 flex justify-center> | ||||
|       <c-button autofocus @click="copy()"> | ||||
|         Copy | ||||
|         {{ t('tools.lorem-ipsum-generator.copyBtn') }} | ||||
|       </c-button> | ||||
|     </div> | ||||
|   </c-card> | ||||
|  | ||||
| @ -1,10 +1,11 @@ | ||||
| import { defineTool } from '../tool'; | ||||
| import n7mIcon from './n7m-icon.svg?component'; | ||||
| import { translate as t } from '@/plugins/i18n.plugin'; | ||||
| 
 | ||||
| export const tool = defineTool({ | ||||
|   name: 'Numeronym generator', | ||||
|   name: t('tools.numeronym-generator.title'), | ||||
|   path: '/numeronym-generator', | ||||
|   description: 'A numeronym is a word where a number is used to form an abbreviation. For example, "i18n" is a numeronym of "internationalization" where 18 stands for the number of letters between the first i and the last n in the word.', | ||||
|   description: t('tools.numeronym-generator.description'), | ||||
|   keywords: ['numeronym', 'generator', 'abbreviation', 'i18n', 'a11y', 'l10n'], | ||||
|   component: () => import('./numeronym-generator.vue'), | ||||
|   icon: n7mIcon, | ||||
|  | ||||
							
								
								
									
										7
									
								
								src/tools/numeronym-generator/locales/en.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/tools/numeronym-generator/locales/en.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| tools: | ||||
|   numeronym-generator: | ||||
|     title: Numeronym generator | ||||
|     description: A numeronym is a word where a number is used to form an abbreviation. For example, "i18n" is a numeronym of "internationalization" where 18 stands for the number of letters between the first i and the last n in the word. | ||||
| 
 | ||||
|     inputPlaceholder: "Enter a word, e.g. 'internationalization'" | ||||
|     outputPlaceholder: "Your numeronym will be here, e.g. 'i18n'" | ||||
							
								
								
									
										7
									
								
								src/tools/numeronym-generator/locales/zh.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/tools/numeronym-generator/locales/zh.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| tools: | ||||
|   numeronym-generator: | ||||
|     title: 数字缩略词生成器 | ||||
|     description: 数字缩略词是一种使用数字形成缩略词的词。例如,"i18n" 是 "internationalization" 的数字缩略词,其中的 18 代表单词中第一个 i 和最后一个 n 之间的字母数量。 | ||||
| 
 | ||||
|     inputPlaceholder: "输入一个单词,例如 'internationalization'" | ||||
|     outputPlaceholder: "您的数字缩略词将显示在此处,例如 'i18n'" | ||||
| @ -1,6 +1,7 @@ | ||||
| <script setup lang="ts"> | ||||
| import { generateNumeronym } from './numeronym-generator.service'; | ||||
| 
 | ||||
| const { t } = useI18n(); | ||||
| const word = ref(''); | ||||
| 
 | ||||
| const numeronym = computed(() => generateNumeronym(word.value)); | ||||
| @ -8,10 +9,10 @@ const numeronym = computed(() => generateNumeronym(word.value)); | ||||
| 
 | ||||
| <template> | ||||
|   <div flex flex-col items-center gap-4> | ||||
|     <c-input-text v-model:value="word" placeholder="Enter a word, e.g. 'internationalization'" size="large" clearable test-id="word-input" /> | ||||
|     <c-input-text v-model:value="word" :placeholder="t('tools.numeronym-generator.inputPlaceholder')" size="large" clearable test-id="word-input" /> | ||||
| 
 | ||||
|     <icon-mdi-arrow-down text-30px /> | ||||
| 
 | ||||
|     <input-copyable :value="numeronym" size="large" readonly placeholder="Your numeronym will be here, e.g. 'i18n'" test-id="numeronym" /> | ||||
|     <input-copyable :value="numeronym" size="large" readonly :placeholder="t('tools.numeronym-generator.outputPlaceholder')" test-id="numeronym" /> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| @ -1,11 +1,11 @@ | ||||
| import { Phone } from '@vicons/tabler'; | ||||
| import { defineTool } from '../tool'; | ||||
| import { translate as t } from '@/plugins/i18n.plugin'; | ||||
| 
 | ||||
| export const tool = defineTool({ | ||||
|   name: 'Phone parser and formatter', | ||||
|   name: t('tools.phone-parser-and-formatter.title'), | ||||
|   path: '/phone-parser-and-formatter', | ||||
|   description: | ||||
|     'Parse, validate and format phone numbers. Get information about the phone number, like the country code, type, etc.', | ||||
|   description: t('tools.phone-parser-and-formatter.description'), | ||||
|   keywords: [ | ||||
|     'phone', | ||||
|     'parser', | ||||
|  | ||||
							
								
								
									
										31
									
								
								src/tools/phone-parser-and-formatter/locales/en.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/tools/phone-parser-and-formatter/locales/en.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| tools: | ||||
|   phone-parser-and-formatter: | ||||
|     title: Phone parser and formatter | ||||
|     description: Parse, validate and format phone numbers. Get information about the phone number, like the country code, type, etc. | ||||
| 
 | ||||
|     country: Country | ||||
|     countryCallingCode: Country calling code | ||||
|     isValid: Is valid? | ||||
|     isPossible: Is possible? | ||||
|     type: Type | ||||
|     internationalFormat: International format | ||||
|     nationalFormat: National format | ||||
|     E164Format: E.164 format | ||||
|     RFC3966Format: RFC3966 format | ||||
|     defaultCountryCode: 'Default country code:' | ||||
|     phoneNumberLabel: 'Phone number:' | ||||
|     phoneNumberPlaceholder: Enter a phone number | ||||
|     unknown: Unknown | ||||
|     mobile: Mobile | ||||
|     fixedLine: Fixed line | ||||
|     fixedLineOrMobile: Fixed line or mobile | ||||
|     personalNumber: Personal number | ||||
|     premiumRate: Premium rate | ||||
|     sharedCost: Shared cost | ||||
|     tollFree: Toll free | ||||
|     UAN: Universal access number | ||||
|     voicemail: Voicemail | ||||
|     VoIP: VoIP | ||||
|     pager: Pager | ||||
| 
 | ||||
|     invalidMessage: Invalid phone number | ||||
							
								
								
									
										31
									
								
								src/tools/phone-parser-and-formatter/locales/zh.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/tools/phone-parser-and-formatter/locales/zh.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| tools: | ||||
|   phone-parser-and-formatter: | ||||
|     title: 电话号码解析和格式化工具 | ||||
|     description: 解析、验证和格式化电话号码。获取有关电话号码的信息,如国家代码、类型等。 | ||||
| 
 | ||||
|     country: 国家 | ||||
|     countryCallingCode: 国家区号 | ||||
|     isValid: 是否有效? | ||||
|     isPossible: 是否可能? | ||||
|     type: 类型 | ||||
|     internationalFormat: 国际格式 | ||||
|     nationalFormat: 国内格式 | ||||
|     E164Format: E.164 格式 | ||||
|     RFC3966Format: RFC3966 格式 | ||||
|     defaultCountryCode: '默认国家代码:' | ||||
|     phoneNumberLabel: '电话号码:' | ||||
|     phoneNumberPlaceholder: 输入电话号码 | ||||
|     unknown: 未知 | ||||
|     mobile: 移动电话 | ||||
|     fixedLine: 固定电话 | ||||
|     fixedLineOrMobile: 固定电话或移动电话 | ||||
|     personalNumber: 个人号码 | ||||
|     premiumRate: 高费率 | ||||
|     sharedCost: 共享费用 | ||||
|     tollFree: 免费电话 | ||||
|     UAN: 通用访问号码 | ||||
|     voicemail: 语音信箱 | ||||
|     VoIP: 互联网电话 | ||||
|     pager: 传呼机 | ||||
| 
 | ||||
|     invalidMessage: 无效的电话号码 | ||||
| @ -1,20 +1,21 @@ | ||||
| import type { CountryCode, NumberType } from 'libphonenumber-js/types'; | ||||
| import lookup from 'country-code-lookup'; | ||||
| import { translate as t } from '@/plugins/i18n.plugin'; | ||||
| 
 | ||||
| export { formatTypeToHumanReadable, getFullCountryName, getDefaultCountryCode }; | ||||
| 
 | ||||
| const typeToLabel: Record<NonNullable<NumberType>, string> = { | ||||
|   MOBILE: 'Mobile', | ||||
|   FIXED_LINE: 'Fixed line', | ||||
|   FIXED_LINE_OR_MOBILE: 'Fixed line or mobile', | ||||
|   PERSONAL_NUMBER: 'Personal number', | ||||
|   PREMIUM_RATE: 'Premium rate', | ||||
|   SHARED_COST: 'Shared cost', | ||||
|   TOLL_FREE: 'Toll free', | ||||
|   UAN: 'Universal access number', | ||||
|   VOICEMAIL: 'Voicemail', | ||||
|   VOIP: 'VoIP', | ||||
|   PAGER: 'Pager', | ||||
|   MOBILE: t('tools.phone-parser-and-formatter.mobile'), | ||||
|   FIXED_LINE: t('tools.phone-parser-and-formatter.fixedLine'), | ||||
|   FIXED_LINE_OR_MOBILE: t('tools.phone-parser-and-formatter.fixedLineOrMobile'), | ||||
|   PERSONAL_NUMBER: t('tools.phone-parser-and-formatter.personalNumber'), | ||||
|   PREMIUM_RATE: t('tools.phone-parser-and-formatter.premiumRate'), | ||||
|   SHARED_COST: t('tools.phone-parser-and-formatter.sharedCost'), | ||||
|   TOLL_FREE: t('tools.phone-parser-and-formatter.tollFree'), | ||||
|   UAN: t('tools.phone-parser-and-formatter.UAN'), | ||||
|   VOICEMAIL: t('tools.phone-parser-and-formatter.voicemail'), | ||||
|   VOIP: t('tools.phone-parser-and-formatter.VoIP'), | ||||
|   PAGER: t('tools.phone-parser-and-formatter.pager'), | ||||
| }; | ||||
| 
 | ||||
| function formatTypeToHumanReadable(type: NumberType): string | undefined { | ||||
|  | ||||
| @ -10,6 +10,7 @@ import { withDefaultOnError } from '@/utils/defaults'; | ||||
| import { booleanToHumanReadable } from '@/utils/boolean'; | ||||
| import { useValidation } from '@/composable/validation'; | ||||
| 
 | ||||
| const { t } = useI18n(); | ||||
| const rawPhone = ref(''); | ||||
| const defaultCountryCode = ref(getDefaultCountryCode()); | ||||
| const validation = useValidation({ | ||||
| @ -17,7 +18,7 @@ const validation = useValidation({ | ||||
|   rules: [ | ||||
|     { | ||||
|       validator: value => value === '' || /^[0-9 +\-()]+$/.test(value), | ||||
|       message: 'Invalid phone number', | ||||
|       message: t('tools.phone-parser-and-formatter.invalidMessage'), | ||||
|     }, | ||||
|   ], | ||||
| }); | ||||
| @ -35,43 +36,43 @@ const parsedDetails = computed(() => { | ||||
| 
 | ||||
|   return [ | ||||
|     { | ||||
|       label: 'Country', | ||||
|       label: t('tools.phone-parser-and-formatter.country'), | ||||
|       value: parsed.country, | ||||
|     }, | ||||
|     { | ||||
|       label: 'Country', | ||||
|       label: t('tools.phone-parser-and-formatter.country'), | ||||
|       value: getFullCountryName(parsed.country), | ||||
|     }, | ||||
|     { | ||||
|       label: 'Country calling code', | ||||
|       label: t('tools.phone-parser-and-formatter.countryCallingCode'), | ||||
|       value: parsed.countryCallingCode, | ||||
|     }, | ||||
|     { | ||||
|       label: 'Is valid?', | ||||
|       label: t('tools.phone-parser-and-formatter.isValid'), | ||||
|       value: booleanToHumanReadable(parsed.isValid()), | ||||
|     }, | ||||
|     { | ||||
|       label: 'Is possible?', | ||||
|       label: t('tools.phone-parser-and-formatter.isPossible'), | ||||
|       value: booleanToHumanReadable(parsed.isPossible()), | ||||
|     }, | ||||
|     { | ||||
|       label: 'Type', | ||||
|       label: t('tools.phone-parser-and-formatter.type'), | ||||
|       value: formatTypeToHumanReadable(parsed.getType()), | ||||
|     }, | ||||
|     { | ||||
|       label: 'International format', | ||||
|       label: t('tools.phone-parser-and-formatter.internationalFormat'), | ||||
|       value: parsed.formatInternational(), | ||||
|     }, | ||||
|     { | ||||
|       label: 'National format', | ||||
|       label: t('tools.phone-parser-and-formatter.nationalFormat'), | ||||
|       value: parsed.formatNational(), | ||||
|     }, | ||||
|     { | ||||
|       label: 'E.164 format', | ||||
|       label: t('tools.phone-parser-and-formatter.E164Format'), | ||||
|       value: parsed.format('E.164'), | ||||
|     }, | ||||
|     { | ||||
|       label: 'RFC3966 format', | ||||
|       label: t('tools.phone-parser-and-formatter.RFC3966Format'), | ||||
|       value: parsed.format('RFC3966'), | ||||
|     }, | ||||
|   ]; | ||||
| @ -85,12 +86,12 @@ const countriesOptions = getCountries().map(code => ({ | ||||
| 
 | ||||
| <template> | ||||
|   <div> | ||||
|     <c-select v-model:value="defaultCountryCode" label="Default country code:" :options="countriesOptions" searchable mb-5 /> | ||||
|     <c-select v-model:value="defaultCountryCode" :label="t('tools.phone-parser-and-formatter.defaultCountryCode')" :options="countriesOptions" searchable mb-5 /> | ||||
| 
 | ||||
|     <c-input-text | ||||
|       v-model:value="rawPhone" | ||||
|       placeholder="Enter a phone number" | ||||
|       label="Phone number:" | ||||
|       :placeholder="t('tools.phone-parser-and-formatter.phoneNumberPlaceholder')" | ||||
|       :label="t('tools.phone-parser-and-formatter.phoneNumberLabel')" | ||||
|       :validation="validation" | ||||
|       mb-5 | ||||
|     /> | ||||
| @ -104,7 +105,7 @@ const countriesOptions = getCountries().map(code => ({ | ||||
|           <td> | ||||
|             <span-copyable v-if="value" :value="value" /> | ||||
|             <span v-else op-70> | ||||
|               Unknown | ||||
|               {{ t('tools.phone-parser-and-formatter.unknown') }} | ||||
|             </span> | ||||
|           </td> | ||||
|         </tr> | ||||
|  | ||||
| @ -1,10 +1,11 @@ | ||||
| import { EyeOff } from '@vicons/tabler'; | ||||
| import { defineTool } from '../tool'; | ||||
| import { translate as t } from '@/plugins/i18n.plugin'; | ||||
| 
 | ||||
| export const tool = defineTool({ | ||||
|   name: 'String obfuscator', | ||||
|   name: t('tools.string-obfuscator.title'), | ||||
|   path: '/string-obfuscator', | ||||
|   description: 'Obfuscate a string (like a secret, an IBAN, or a token) to make it shareable and identifiable without revealing its content.', | ||||
|   description: t('tools.string-obfuscator.description'), | ||||
|   keywords: ['string', 'obfuscator', 'secret', 'token', 'hide', 'obscure', 'mask', 'masking'], | ||||
|   component: () => import('./string-obfuscator.vue'), | ||||
|   icon: EyeOff, | ||||
|  | ||||
							
								
								
									
										10
									
								
								src/tools/string-obfuscator/locales/en.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/tools/string-obfuscator/locales/en.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| tools: | ||||
|   string-obfuscator: | ||||
|     title: String obfuscator | ||||
|     description: Obfuscate a string (like a secret, an IBAN, or a token) to make it shareable and identifiable without revealing its content. | ||||
| 
 | ||||
|     inputLabel: 'String to obfuscate:' | ||||
|     inputPlaceholder: Enter string to obfuscate | ||||
|     keepFirst: 'Keep first:' | ||||
|     keepLast: 'Keep last:' | ||||
|     KeepSpaces: 'Keep spaces:' | ||||
							
								
								
									
										10
									
								
								src/tools/string-obfuscator/locales/zh.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/tools/string-obfuscator/locales/zh.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| tools: | ||||
|   string-obfuscator: | ||||
|     title: 字符串混淆器 | ||||
|     description: 将字符串(如密码、IBAN 或令牌)混淆,使其可共享和识别,而不会暴露内容。 | ||||
| 
 | ||||
|     inputLabel: '要混淆的字符串:' | ||||
|     inputPlaceholder: 输入要混淆的字符串 | ||||
|     keepFirst: '保留前:' | ||||
|     keepLast: '保留后:' | ||||
|     KeepSpaces: '保留空格:' | ||||
| @ -2,6 +2,7 @@ | ||||
| import { useObfuscateString } from './string-obfuscator.model'; | ||||
| import { useCopy } from '@/composable/copy'; | ||||
| 
 | ||||
| const { t } = useI18n(); | ||||
| const str = ref('Lorem ipsum dolor sit amet'); | ||||
| const keepFirst = ref(4); | ||||
| const keepLast = ref(4); | ||||
| @ -13,22 +14,22 @@ const { copy } = useCopy({ source: obfuscatedString }); | ||||
| 
 | ||||
| <template> | ||||
|   <div> | ||||
|     <c-input-text v-model:value="str" raw-text placeholder="Enter string to obfuscate" label="String to obfuscate:" clearable multiline /> | ||||
|     <c-input-text v-model:value="str" raw-text :placeholder="t('tools.string-obfuscator.inputPlaceholder')" :label="t('tools.string-obfuscator.inputLabel')" clearable multiline /> | ||||
| 
 | ||||
|     <div mt-4 flex gap-10px> | ||||
|       <div> | ||||
|         <div>Keep first:</div> | ||||
|         <div>{{ t('tools.string-obfuscator.keepFirst') }}</div> | ||||
|         <n-input-number v-model:value="keepFirst" min="0" /> | ||||
|       </div> | ||||
| 
 | ||||
|       <div> | ||||
|         <div>Keep last:</div> | ||||
|         <div>{{ t('tools.string-obfuscator.keepLast') }}</div> | ||||
|         <n-input-number v-model:value="keepLast" min="0" /> | ||||
|       </div> | ||||
| 
 | ||||
|       <div> | ||||
|         <div mb-5px> | ||||
|           Keep spaces: | ||||
|           {{ t('tools.string-obfuscator.KeepSpaces') }} | ||||
|         </div> | ||||
|         <n-switch v-model:value="keepSpace" /> | ||||
|       </div> | ||||
|  | ||||
| @ -1,11 +1,11 @@ | ||||
| import { Temperature } from '@vicons/tabler'; | ||||
| import { defineTool } from '../tool'; | ||||
| import { translate as t } from '@/plugins/i18n.plugin'; | ||||
| 
 | ||||
| export const tool = defineTool({ | ||||
|   name: 'Temperature converter', | ||||
|   name: t('tools.temperature-converter.title'), | ||||
|   path: '/temperature-converter', | ||||
|   description: | ||||
|     'Temperature degrees conversions for Kelvin, Celsius, Fahrenheit, Rankine, Delisle, Newton, Réaumur and Rømer.', | ||||
|   description: t('tools.temperature-converter.description'), | ||||
|   keywords: [ | ||||
|     'temperature', | ||||
|     'converter', | ||||
|  | ||||
							
								
								
									
										13
									
								
								src/tools/temperature-converter/locales/en.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/tools/temperature-converter/locales/en.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| tools: | ||||
|   temperature-converter: | ||||
|     title: Temperature converter | ||||
|     description: Temperature degrees conversions for Kelvin, Celsius, Fahrenheit, Rankine, Delisle, Newton, Réaumur and Rømer. | ||||
| 
 | ||||
|     kelvin: Kelvin | ||||
|     celsius: Celsius | ||||
|     fahrenheit: Fahrenheit | ||||
|     rankine: Rankine | ||||
|     delisle: Delisle | ||||
|     newton: Newton | ||||
|     reaumur: Réaumur | ||||
|     romer: Rømer | ||||
							
								
								
									
										13
									
								
								src/tools/temperature-converter/locales/zh.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/tools/temperature-converter/locales/zh.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| tools: | ||||
|   temperature-converter: | ||||
|     title: 温度转换器 | ||||
|     description: 开尔文、摄氏度、华氏度、兰氏度、德利斯尔度、牛顿度、瑞摄姆度和罗默度的温度转换。 | ||||
| 
 | ||||
|     kelvin: 开尔文 | ||||
|     celsius: 摄氏度 | ||||
|     fahrenheit: 华氏度 | ||||
|     rankine: 兰氏度 | ||||
|     delisle: 德利斯尔度 | ||||
|     newton: 牛顿度 | ||||
|     reaumur: 瑞摄姆度 | ||||
|     romer: 罗默度 | ||||
| @ -19,6 +19,7 @@ import { | ||||
| 
 | ||||
| type TemperatureScale = 'kelvin' | 'celsius' | 'fahrenheit' | 'rankine' | 'delisle' | 'newton' | 'reaumur' | 'romer'; | ||||
| 
 | ||||
| const { t } = useI18n(); | ||||
| const units = reactive< | ||||
|   Record< | ||||
|     string | TemperatureScale, | ||||
| @ -26,56 +27,56 @@ const units = reactive< | ||||
|   > | ||||
|       >({ | ||||
|         kelvin: { | ||||
|           title: 'Kelvin', | ||||
|           title: t('tools.temperature-converter.kelvin'), | ||||
|           unit: 'K', | ||||
|           ref: 0, | ||||
|           toKelvin: _.identity, | ||||
|           fromKelvin: _.identity, | ||||
|         }, | ||||
|         celsius: { | ||||
|           title: 'Celsius', | ||||
|           title: t('tools.temperature-converter.celsius'), | ||||
|           unit: '°C', | ||||
|           ref: 0, | ||||
|           toKelvin: convertCelsiusToKelvin, | ||||
|           fromKelvin: convertKelvinToCelsius, | ||||
|         }, | ||||
|         fahrenheit: { | ||||
|           title: 'Fahrenheit', | ||||
|           title: t('tools.temperature-converter.fahrenheit'), | ||||
|           unit: '°F', | ||||
|           ref: 0, | ||||
|           toKelvin: convertFahrenheitToKelvin, | ||||
|           fromKelvin: convertKelvinToFahrenheit, | ||||
|         }, | ||||
|         rankine: { | ||||
|           title: 'Rankine', | ||||
|           title: t('tools.temperature-converter.rankine'), | ||||
|           unit: '°R', | ||||
|           ref: 0, | ||||
|           toKelvin: convertRankineToKelvin, | ||||
|           fromKelvin: convertKelvinToRankine, | ||||
|         }, | ||||
|         delisle: { | ||||
|           title: 'Delisle', | ||||
|           title: t('tools.temperature-converter.delisle'), | ||||
|           unit: '°De', | ||||
|           ref: 0, | ||||
|           toKelvin: convertDelisleToKelvin, | ||||
|           fromKelvin: convertKelvinToDelisle, | ||||
|         }, | ||||
|         newton: { | ||||
|           title: 'Newton', | ||||
|           title: t('tools.temperature-converter.newton'), | ||||
|           unit: '°N', | ||||
|           ref: 0, | ||||
|           toKelvin: convertNewtonToKelvin, | ||||
|           fromKelvin: convertKelvinToNewton, | ||||
|         }, | ||||
|         reaumur: { | ||||
|           title: 'Réaumur', | ||||
|           title: t('tools.temperature-converter.reaumur'), | ||||
|           unit: '°Ré', | ||||
|           ref: 0, | ||||
|           toKelvin: convertReaumurToKelvin, | ||||
|           fromKelvin: convertKelvinToReaumur, | ||||
|         }, | ||||
|         romer: { | ||||
|           title: 'Rømer', | ||||
|           title: t('tools.temperature-converter.romer'), | ||||
|           unit: '°Rø', | ||||
|           ref: 0, | ||||
|           toKelvin: convertRomerToKelvin, | ||||
|  | ||||
| @ -1,10 +1,11 @@ | ||||
| import { FileDiff } from '@vicons/tabler'; | ||||
| import { defineTool } from '../tool'; | ||||
| import { translate as t } from '@/plugins/i18n.plugin'; | ||||
| 
 | ||||
| export const tool = defineTool({ | ||||
|   name: 'Text diff', | ||||
|   name: t('tools.text-diff.title'), | ||||
|   path: '/text-diff', | ||||
|   description: 'Compare two texts and see the differences between them.', | ||||
|   description: t('tools.text-diff.description'), | ||||
|   keywords: ['text', 'diff', 'compare', 'string', 'text diff', 'code'], | ||||
|   component: () => import('./text-diff.vue'), | ||||
|   icon: FileDiff, | ||||
|  | ||||
							
								
								
									
										4
									
								
								src/tools/text-diff/locales/en.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/tools/text-diff/locales/en.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| tools: | ||||
|   text-diff: | ||||
|     title: Text diff | ||||
|     description: Compare two texts and see the differences between them. | ||||
							
								
								
									
										4
									
								
								src/tools/text-diff/locales/zh.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/tools/text-diff/locales/zh.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| tools: | ||||
|   text-diff: | ||||
|     title: 文本差异 | ||||
|     description: 比较两个文本并查看它们之间的差异。 | ||||
| @ -1,10 +1,11 @@ | ||||
| import { FileText } from '@vicons/tabler'; | ||||
| import { defineTool } from '../tool'; | ||||
| import { translate as t } from '@/plugins/i18n.plugin'; | ||||
| 
 | ||||
| export const tool = defineTool({ | ||||
|   name: 'Text statistics', | ||||
|   name: t('tools.text-statistics.title'), | ||||
|   path: '/text-statistics', | ||||
|   description: 'Get information about a text, the amount of characters, the amount of words, it\'s size, ...', | ||||
|   description: t('tools.text-statistics.description'), | ||||
|   keywords: ['text', 'statistics', 'length', 'characters', 'count', 'size', 'bytes'], | ||||
|   component: () => import('./text-statistics.vue'), | ||||
|   icon: FileText, | ||||
|  | ||||
							
								
								
									
										10
									
								
								src/tools/text-statistics/locales/en.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/tools/text-statistics/locales/en.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| tools: | ||||
|   text-statistics: | ||||
|     title: Text statistics | ||||
|     description: Get information about a text, the amount of characters, the amount of words, it's size, ... | ||||
| 
 | ||||
|     inputPlaceholder: Your text... | ||||
|     characterCount: Character count | ||||
|     wordCount: Word count | ||||
|     lineCount: Line count | ||||
|     byteSize: Byte size | ||||
							
								
								
									
										10
									
								
								src/tools/text-statistics/locales/zh.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/tools/text-statistics/locales/zh.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| tools: | ||||
|   text-statistics: | ||||
|     title: 文本统计 | ||||
|     description: 获取文本信息,包括字符数、单词数、大小等... | ||||
| 
 | ||||
|     inputPlaceholder: 请输入文本... | ||||
|     characterCount: 字符数 | ||||
|     wordCount: 单词数 | ||||
|     lineCount: 行数 | ||||
|     byteSize: 字节大小 | ||||
| @ -2,18 +2,19 @@ | ||||
| import { getStringSizeInBytes } from './text-statistics.service'; | ||||
| import { formatBytes } from '@/utils/convert'; | ||||
| 
 | ||||
| const { t } = useI18n(); | ||||
| const text = ref(''); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <c-card> | ||||
|     <c-input-text v-model:value="text" multiline placeholder="Your text..." rows="5" /> | ||||
|     <c-input-text v-model:value="text" multiline :placeholder="t('tools.text-statistics.inputPlaceholder')" rows="5" /> | ||||
| 
 | ||||
|     <div mt-5 flex> | ||||
|       <n-statistic label="Character count" :value="text.length" flex-1 /> | ||||
|       <n-statistic label="Word count" :value="text === '' ? 0 : text.split(/\s+/).length" flex-1 /> | ||||
|       <n-statistic label="Line count" :value="text === '' ? 0 : text.split(/\r\n|\r|\n/).length" flex-1 /> | ||||
|       <n-statistic label="Byte size" :value="formatBytes(getStringSizeInBytes(text))" flex-1 /> | ||||
|       <n-statistic :label="t('tools.text-statistics.characterCount')" :value="text.length" flex-1 /> | ||||
|       <n-statistic :label="t('tools.text-statistics.wordCount')" :value="text === '' ? 0 : text.split(/\s+/).length" flex-1 /> | ||||
|       <n-statistic :label="t('tools.text-statistics.lineCount')" :value="text === '' ? 0 : text.split(/\r\n|\r|\n/).length" flex-1 /> | ||||
|       <n-statistic :label="t('tools.text-statistics.byteSize')" :value="formatBytes(getStringSizeInBytes(text))" flex-1 /> | ||||
|     </div> | ||||
|   </c-card> | ||||
| </template> | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user