mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-05-05 13:57:10 -04:00
feat(translate): Complete translation of all tools
This commit is contained in:
parent
a2e498d0aa
commit
ae71454a38
47 changed files with 456 additions and 120 deletions
|
@ -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…
Add table
Add a link
Reference in a new issue