Skip to content

Commit 7cec37b

Browse files
authored
Merge pull request #297 from iib0011/fix-278
Fix 278
2 parents f3c5946 + 80662c5 commit 7cec37b

File tree

7 files changed

+358
-0
lines changed

7 files changed

+358
-0
lines changed

public/locales/en/string.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,5 +307,21 @@
307307
"shortDescription": "Quickly URL-escape a string.",
308308
"title": "String URL encoder"
309309
}
310+
},
311+
"unicode": {
312+
"title": "Unicode Encoder / Decoder",
313+
"inputTitle": "Input",
314+
"resultTitle": "Processed Output",
315+
"optionsTitle": "Mode",
316+
"caseOptionsTitle": "Case Options",
317+
"encode": "Encode",
318+
"decode": "Decode",
319+
"uppercase": "Uppercase Hex",
320+
"description": "Convert text to Unicode escape sequences or decode them back to readable text.",
321+
"shortDescription": "Encode or decode text using Unicode escape sequences.",
322+
"toolInfo": {
323+
"title": "Unicode Encoder / Decoder",
324+
"description": "This tool lets you convert plain text into Unicode escape sequences (e.g., \\uXXXX) and decode Unicode escape sequences back into standard text. You can also choose whether the hexadecimal output is formatted in uppercase or lowercase when encoding."
325+
}
310326
}
311327
}

src/pages/tools/string/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { tool as stringCensor } from './censor/meta';
2121
import { tool as stringPasswordGenerator } from './password-generator/meta';
2222
import { tool as stringEncodeUrl } from './url-encode/meta';
2323
import { tool as StringDecodeUrl } from './url-decode/meta';
24+
import { tool as stringUnicode } from './unicode/meta';
2425

2526
export const stringTools = [
2627
stringSplit,
@@ -45,5 +46,6 @@ export const stringTools = [
4546
stringPasswordGenerator,
4647
stringEncodeUrl,
4748
StringDecodeUrl,
49+
stringUnicode,
4850
stringHiddenCharacterDetector
4951
];
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import { Box } from '@mui/material';
2+
import { useState } from 'react';
3+
import ToolContent from '@components/ToolContent';
4+
import { ToolComponentProps } from '@tools/defineTool';
5+
import ToolTextInput from '@components/input/ToolTextInput';
6+
import ToolTextResult from '@components/result/ToolTextResult';
7+
import { GetGroupsType } from '@components/options/ToolOptions';
8+
import { CardExampleType } from '@components/examples/ToolExamples';
9+
import { unicode } from './service';
10+
import { InitialValuesType } from './types';
11+
import SimpleRadio from '@components/options/SimpleRadio';
12+
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
13+
import { useTranslation } from 'react-i18next';
14+
15+
const initialValues: InitialValuesType = {
16+
mode: 'encode',
17+
uppercase: false
18+
};
19+
20+
const exampleCards: CardExampleType<InitialValuesType>[] = [
21+
{
22+
title: 'Encode to Unicode Escape',
23+
description: 'Encode plain text to Unicode escape sequences.',
24+
sampleText: 'Hello, World!',
25+
sampleResult:
26+
'\\u0048\\u0065\\u006c\\u006c\\u006f\\u002c\\u0020\\u0057\\u006f\\u0072\\u006c\\u0064\\u0021',
27+
sampleOptions: {
28+
mode: 'encode',
29+
uppercase: false
30+
}
31+
},
32+
{
33+
title: 'Encode to Unicode Escape (Uppercase)',
34+
description: 'Encode plain text to uppercase Unicode escape sequences.',
35+
sampleText: 'Hello, World!',
36+
sampleResult:
37+
'\\u0048\\u0065\\u006c\\u006c\\u006f\\u002c\\u0020\\u0057\\u006f\\u0072\\u006c\\u0064\\u0021'.toUpperCase(),
38+
sampleOptions: {
39+
mode: 'encode',
40+
uppercase: true
41+
}
42+
},
43+
{
44+
title: 'Decode Unicode Escape',
45+
description: 'Decode Unicode escape sequences back to plain text.',
46+
sampleText:
47+
'\\u0048\\u0065\\u006c\\u006c\\u006f\\u002c\\u0020\\u0057\\u006f\\u0072\\u006c\\u0064\\u0021',
48+
sampleResult: 'Hello, World!',
49+
sampleOptions: {
50+
mode: 'decode',
51+
uppercase: false
52+
}
53+
}
54+
];
55+
export default function Unicode({ title }: ToolComponentProps) {
56+
const { t } = useTranslation('string');
57+
const [input, setInput] = useState<string>('');
58+
const [result, setResult] = useState<string>('');
59+
60+
const compute = (values: InitialValuesType, input: string) => {
61+
setResult(unicode(input, values));
62+
};
63+
64+
const getGroups: GetGroupsType<InitialValuesType> = ({
65+
values,
66+
updateField
67+
}) => [
68+
{
69+
title: t('unicode.optionsTitle'),
70+
component: (
71+
<Box>
72+
<SimpleRadio
73+
onClick={() => updateField('mode', 'encode')}
74+
checked={values.mode === 'encode'}
75+
title={t('unicode.encode')}
76+
/>
77+
<SimpleRadio
78+
onClick={() => updateField('mode', 'decode')}
79+
checked={values.mode === 'decode'}
80+
title={t('unicode.decode')}
81+
/>
82+
</Box>
83+
)
84+
},
85+
{
86+
title: t('unicode.caseOptionsTitle'),
87+
component: (
88+
<Box>
89+
<CheckboxWithDesc
90+
checked={values.uppercase}
91+
onChange={(value) => updateField('uppercase', value)}
92+
title={t('unicode.uppercase')}
93+
/>
94+
</Box>
95+
)
96+
}
97+
];
98+
return (
99+
<ToolContent
100+
title={title}
101+
input={input}
102+
inputComponent={
103+
<ToolTextInput
104+
value={input}
105+
onChange={setInput}
106+
title={t('unicode.inputTitle')}
107+
/>
108+
}
109+
resultComponent={
110+
<ToolTextResult value={result} title={t('unicode.resultTitle')} />
111+
}
112+
initialValues={initialValues}
113+
exampleCards={exampleCards}
114+
getGroups={getGroups}
115+
setInput={setInput}
116+
compute={compute}
117+
toolInfo={{
118+
title: t('unicode.toolInfo.title'),
119+
description: t('unicode.toolInfo.description')
120+
}}
121+
/>
122+
);
123+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { defineTool } from '@tools/defineTool';
2+
import { lazy } from 'react';
3+
4+
export const tool = defineTool('string', {
5+
i18n: {
6+
name: 'string:unicode.title',
7+
description: 'string:unicode.description',
8+
shortDescription: 'string:unicode.shortDescription'
9+
},
10+
path: 'unicode',
11+
icon: 'mdi:unicode',
12+
keywords: ['unicode', 'encode', 'decode', 'escape', 'text'],
13+
component: lazy(() => import('./index'))
14+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { InitialValuesType } from './types';
2+
3+
export function unicode(input: string, options: InitialValuesType): string {
4+
if (!input) return '';
5+
if (options.mode === 'encode') {
6+
let result = '';
7+
for (let i = 0; i < input.length; i++) {
8+
let hex = input.charCodeAt(i).toString(16);
9+
hex = ('0000' + hex).slice(-4);
10+
if (options.uppercase) {
11+
hex = hex.toUpperCase();
12+
}
13+
result += '\\u' + hex;
14+
}
15+
return result;
16+
} else {
17+
return input.replace(/\\u([\dA-Fa-f]{4})/g, (match, grp) => {
18+
return String.fromCharCode(parseInt(grp, 16));
19+
});
20+
}
21+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export type InitialValuesType = {
2+
mode: 'encode' | 'decode';
3+
uppercase: boolean;
4+
};
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
import { expect, describe, it } from 'vitest';
2+
import { unicode } from './service';
3+
4+
describe('unicode', () => {
5+
it('should encode an English string to lowercase hex correctly', () => {
6+
const input = 'Hello, World!';
7+
const result = unicode(input, { mode: 'encode', uppercase: false });
8+
expect(result).toBe(
9+
'\\u0048\\u0065\\u006c\\u006c\\u006f\\u002c\\u0020\\u0057\\u006f\\u0072\\u006c\\u0064\\u0021'
10+
);
11+
});
12+
13+
it('should encode an English string to uppercase hex correctly', () => {
14+
const input = 'Hello, World!';
15+
const result = unicode(input, { mode: 'encode', uppercase: true });
16+
expect(result).toBe(
17+
'\\u0048\\u0065\\u006C\\u006C\\u006F\\u002C\\u0020\\u0057\\u006F\\u0072\\u006C\\u0064\\u0021'
18+
);
19+
});
20+
21+
it('should decode an English lowercase hex string correctly', () => {
22+
const input =
23+
'\\u0048\\u0065\\u006c\\u006c\\u006f\\u002c\\u0020\\u0057\\u006f\\u0072\\u006c\\u0064\\u0021';
24+
const result = unicode(input, { mode: 'decode', uppercase: false });
25+
expect(result).toBe('Hello, World!');
26+
});
27+
28+
it('should decode an English uppercase hex string correctly', () => {
29+
const input =
30+
'\\u0048\\u0065\\u006C\\u006C\\u006F\\u002C\\u0020\\u0057\\u006F\\u0072\\u006C\\u0064\\u0021';
31+
const result = unicode(input, { mode: 'decode', uppercase: false });
32+
expect(result).toBe('Hello, World!');
33+
});
34+
35+
it('should encode a Korean string to lowercase hex correctly', () => {
36+
const input = '안녕하세요, 세계!';
37+
const result = unicode(input, { mode: 'encode', uppercase: false });
38+
expect(result).toBe(
39+
'\\uc548\\ub155\\ud558\\uc138\\uc694\\u002c\\u0020\\uc138\\uacc4\\u0021'
40+
);
41+
});
42+
43+
it('should encode a Korean string to uppercase hex correctly', () => {
44+
const input = '안녕하세요, 세계!';
45+
const result = unicode(input, { mode: 'encode', uppercase: true });
46+
expect(result).toBe(
47+
'\\uC548\\uB155\\uD558\\uC138\\uC694\\u002C\\u0020\\uC138\\uACC4\\u0021'
48+
);
49+
});
50+
51+
it('should decode a Korean lowercase hex string correctly', () => {
52+
const input =
53+
'\\uc548\\ub155\\ud558\\uc138\\uc694\\u002c\\u0020\\uc138\\uacc4\\u0021';
54+
const result = unicode(input, { mode: 'decode', uppercase: false });
55+
expect(result).toBe('안녕하세요, 세계!');
56+
});
57+
58+
it('should decode a Korean uppercase hex string correctly', () => {
59+
const input =
60+
'\\uC548\\uB155\\uD558\\uC138\\uC694\\u002C\\u0020\\uC138\\uACC4\\u0021';
61+
const result = unicode(input, { mode: 'decode', uppercase: false });
62+
expect(result).toBe('안녕하세요, 세계!');
63+
});
64+
65+
it('should encode a Japanese string to lowercase hex correctly', () => {
66+
const input = 'こんにちは、世界!';
67+
const result = unicode(input, { mode: 'encode', uppercase: false });
68+
expect(result).toBe(
69+
'\\u3053\\u3093\\u306b\\u3061\\u306f\\u3001\\u4e16\\u754c\\uff01'
70+
);
71+
});
72+
73+
it('should encode a Japanese string to uppercase hex correctly', () => {
74+
const input = 'こんにちは、世界!';
75+
const result = unicode(input, { mode: 'encode', uppercase: true });
76+
expect(result).toBe(
77+
'\\u3053\\u3093\\u306B\\u3061\\u306F\\u3001\\u4E16\\u754C\\uFF01'
78+
);
79+
});
80+
81+
it('should decode a Japanese lowercase hex string correctly', () => {
82+
const input =
83+
'\\u3053\\u3093\\u306b\\u3061\\u306f\\u3001\\u4e16\\u754c\\uff01';
84+
const result = unicode(input, { mode: 'decode', uppercase: false });
85+
expect(result).toBe('こんにちは、世界!');
86+
});
87+
88+
it('should decode a Japanese uppercase hex string correctly', () => {
89+
const input =
90+
'\\u3053\\u3093\\u306B\\u3061\\u306F\\u3001\\u4E16\\u754C\\uFF01';
91+
const result = unicode(input, { mode: 'decode', uppercase: false });
92+
expect(result).toBe('こんにちは、世界!');
93+
});
94+
95+
it('should encode a Chinese string to lowercase hex correctly', () => {
96+
const input = '你好,世界!';
97+
const result = unicode(input, { mode: 'encode', uppercase: false });
98+
expect(result).toBe('\\u4f60\\u597d\\uff0c\\u4e16\\u754c\\uff01');
99+
});
100+
101+
it('should encode a Chinese string to uppercase hex correctly', () => {
102+
const input = '你好,世界!';
103+
const result = unicode(input, { mode: 'encode', uppercase: true });
104+
expect(result).toBe('\\u4F60\\u597D\\uFF0C\\u4E16\\u754C\\uFF01');
105+
});
106+
107+
it('should decode a Chinese lowercase hex string correctly', () => {
108+
const input = '\\u4f60\\u597d\\uff0c\\u4e16\\u754c\\uff01';
109+
const result = unicode(input, { mode: 'decode', uppercase: false });
110+
expect(result).toBe('你好,世界!');
111+
});
112+
113+
it('should decode a Chinese uppercase hex string correctly', () => {
114+
const input = '\\u4F60\\u597D\\uFF0C\\u4E16\\u754C\\uFF01';
115+
const result = unicode(input, { mode: 'decode', uppercase: false });
116+
expect(result).toBe('你好,世界!');
117+
});
118+
119+
it('should encode a Russian string to lowercase hex correctly', () => {
120+
const input = 'Привет, мир!';
121+
const result = unicode(input, { mode: 'encode', uppercase: false });
122+
expect(result).toBe(
123+
'\\u041f\\u0440\\u0438\\u0432\\u0435\\u0442\\u002c\\u0020\\u043c\\u0438\\u0440\\u0021'
124+
);
125+
});
126+
127+
it('should encode a Russian string to uppercase hex correctly', () => {
128+
const input = 'Привет, мир!';
129+
const result = unicode(input, { mode: 'encode', uppercase: true });
130+
expect(result).toBe(
131+
'\\u041F\\u0440\\u0438\\u0432\\u0435\\u0442\\u002C\\u0020\\u043C\\u0438\\u0440\\u0021'
132+
);
133+
});
134+
135+
it('should decode a Russian lowercase hex string correctly', () => {
136+
const input =
137+
'\\u041f\\u0440\\u0438\\u0432\\u0435\\u0442\\u002c\\u0020\\u043c\\u0438\\u0440\\u0021';
138+
const result = unicode(input, { mode: 'decode', uppercase: false });
139+
expect(result).toBe('Привет, мир!');
140+
});
141+
142+
it('should decode a Russian uppercase hex string correctly', () => {
143+
const input =
144+
'\\u041F\\u0440\\u0438\\u0432\\u0435\\u0442\\u002C\\u0020\\u043C\\u0438\\u0440\\u0021';
145+
const result = unicode(input, { mode: 'decode', uppercase: false });
146+
expect(result).toBe('Привет, мир!');
147+
});
148+
149+
it('should encode a Spanish string to lowercase hex correctly', () => {
150+
const input = '¡Hola, Mundo!';
151+
const result = unicode(input, { mode: 'encode', uppercase: false });
152+
expect(result).toBe(
153+
'\\u00a1\\u0048\\u006f\\u006c\\u0061\\u002c\\u0020\\u004d\\u0075\\u006e\\u0064\\u006f\\u0021'
154+
);
155+
});
156+
157+
it('should encode a Spanish string to uppercase hex correctly', () => {
158+
const input = '¡Hola, Mundo!';
159+
const result = unicode(input, { mode: 'encode', uppercase: true });
160+
expect(result).toBe(
161+
'\\u00A1\\u0048\\u006F\\u006C\\u0061\\u002C\\u0020\\u004D\\u0075\\u006E\\u0064\\u006F\\u0021'
162+
);
163+
});
164+
165+
it('should decode a Spanish lowercase hex string correctly', () => {
166+
const input =
167+
'\\u00a1\\u0048\\u006f\\u006c\\u0061\\u002c\\u0020\\u004d\\u0075\\u006e\\u0064\\u006f\\u0021';
168+
const result = unicode(input, { mode: 'decode', uppercase: false });
169+
expect(result).toBe('¡Hola, Mundo!');
170+
});
171+
172+
it('should decode a Spanish uppercase hex string correctly', () => {
173+
const input =
174+
'\\u00A1\\u0048\\u006F\\u006C\\u0061\\u002C\\u0020\\u004D\\u0075\\u006E\\u0064\\u006F\\u0021';
175+
const result = unicode(input, { mode: 'decode', uppercase: false });
176+
expect(result).toBe('¡Hola, Mundo!');
177+
});
178+
});

0 commit comments

Comments
 (0)