Skip to content

Commit ff84592

Browse files
committed
chore: translate all Vietnamese UI strings and comments to English
Affects three files: - src/features/T9Converter.jsx (32 strings): panel labels, placeholders, validation messages, button text, decode/encode cheat-sheets, example notes, button titles - src/features/StringTools.jsx (21 strings): codec descriptions, JWT error messages, char counters, button titles, placeholders, QuickRef section header - src/features/DataTablePreview/DataTablePreview.jsx (3 comments) No behavior or logic changes; grep for Vietnamese diacritics in src/ now returns zero matches. npm run build passes.
1 parent 25e5331 commit ff84592

3 files changed

Lines changed: 56 additions & 56 deletions

File tree

src/features/DataTablePreview/DataTablePreview.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@ const DataTablePreview = () => {
102102
const handlePasteChange = (e) => {
103103
const value = e.target.value;
104104
setRawInput(value);
105-
// Không auto-parse, chỉ lưu raw text
106-
// User phải nhấn nút Parse hoặc Ctrl+Enter
105+
// Do not auto-parse; just store raw text
106+
// User must click Parse or press Ctrl+Enter
107107
};
108108

109109
const handlePasteKeyDown = (e) => {
@@ -120,7 +120,7 @@ const DataTablePreview = () => {
120120
const handleDelimiterChange = (e) => {
121121
const newDelimiter = e.target.value;
122122
setDelimiter(newDelimiter);
123-
// Nếu đã có data, re-parse với delimiter mới
123+
// If data already exists, re-parse with the new delimiter
124124
if (data.length > 0 && activeTab === 'paste' && rawInput) {
125125
parseData(rawInput, newDelimiter);
126126
}

src/features/StringTools.jsx

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,10 @@ const decodeUnicode = (str) => {
8585
return str.replace(/\\[Uu]([0-9a-fA-F]{4,8})/g, (_, h) => String.fromCodePoint(parseInt(h, 16)));
8686
};
8787

88-
const encodeJwt = (str) => { throw new Error('JWT encode không áp dụng, dùng JWT Debugger'); };
88+
const encodeJwt = (str) => { throw new Error('JWT encode is not supported, use JWT Debugger'); };
8989
const decodeJwt = (str) => {
9090
const parts = str.trim().split('.');
91-
if (parts.length !== 3) throw new Error('JWT không hợp lệ, cần 3 phần ngăn cách bởi dấu chấm');
91+
if (parts.length !== 3) throw new Error('Invalid JWT, expected 3 parts separated by dots');
9292
const decode = p => {
9393
let s = p.replace(/-/g, '+').replace(/_/g, '/');
9494
while (s.length % 4) s += '=';
@@ -104,71 +104,71 @@ const CODECS = [
104104
id: 'base64',
105105
label: 'Base64',
106106
color: 'blue',
107-
description: 'Encode/Decode chuỗi text sang Base64 (hỗ trợ Unicode)',
107+
description: 'Encode/Decode text to Base64 (Unicode supported)',
108108
encode: encodeBase64,
109109
decode: decodeBase64,
110110
},
111111
{
112112
id: 'base64url',
113113
label: 'Base64 URL Safe',
114114
color: 'indigo',
115-
description: 'Base64 biến thể an toàn cho URL (thay +/ bằng -_)',
115+
description: 'URL-safe Base64 variant (replaces +/ with -_)',
116116
encode: encodeBase64Safe,
117117
decode: decodeBase64Safe,
118118
},
119119
{
120120
id: 'url',
121121
label: 'URL Encode',
122122
color: 'violet',
123-
description: 'Encode ký tự đặc biệt cho query string (encodeURIComponent)',
123+
description: 'Encode special characters for query string (encodeURIComponent)',
124124
encode: encodeUrl,
125125
decode: decodeUrl,
126126
},
127127
{
128128
id: 'urlfull',
129129
label: 'URL Full Encode',
130130
color: 'purple',
131-
description: 'Encode toàn bộ ký tự thành %XX (kể cả ASCII)',
131+
description: 'Encode every character as %XX (including ASCII)',
132132
encode: encodeUrlFull,
133133
decode: decodeUrl,
134134
},
135135
{
136136
id: 'html',
137137
label: 'HTML Entities',
138138
color: 'amber',
139-
description: 'Chuyển đổi &, <, >, ", \' thành HTML entities',
139+
description: 'Convert &, <, >, \", \' to HTML entities',
140140
encode: encodeHtml,
141141
decode: decodeHtml,
142142
},
143143
{
144144
id: 'hex',
145145
label: 'Hex',
146146
color: 'green',
147-
description: 'Encode bytes thành chuỗi hexadecimal',
147+
description: 'Encode bytes as a hexadecimal string',
148148
encode: encodeHex,
149149
decode: decodeHex,
150150
},
151151
{
152152
id: 'binary',
153153
label: 'Binary',
154154
color: 'teal',
155-
description: 'Chuyển text thành chuỗi nhị phân 0/1',
155+
description: 'Convert text to a 0/1 binary string',
156156
encode: encodeBinary,
157157
decode: decodeBinary,
158158
},
159159
{
160160
id: 'unicode',
161161
label: 'Unicode Escape',
162162
color: 'cyan',
163-
description: 'Chuyển ký tự thành \\uXXXX hoặc \\UXXXXXXXX',
163+
description: 'Convert characters to \\uXXXX or \\UXXXXXXXX',
164164
encode: encodeUnicode,
165165
decode: decodeUnicode,
166166
},
167167
{
168168
id: 'jwt',
169169
label: 'JWT Decode',
170170
color: 'rose',
171-
description: 'Giải mã JWT payload (không verify signature)',
171+
description: 'Decode JWT payload (signature not verified)',
172172
encode: null,
173173
decode: decodeJwt,
174174
},
@@ -272,10 +272,10 @@ const StringTools = () => {
272272
if (input) {
273273
try {
274274
if (direction === 'encode') {
275-
if (!codec.encode) throw new Error(`${codec.label} không hỗ trợ encode`);
275+
if (!codec.encode) throw new Error(`${codec.label} does not support encode`);
276276
output = codec.encode(input);
277277
} else {
278-
if (!codec.decode) throw new Error(`${codec.label} không hỗ trợ decode`);
278+
if (!codec.decode) throw new Error(`${codec.label} does not support decode`);
279279
output = codec.decode(input);
280280
}
281281
} catch (e) {
@@ -314,8 +314,8 @@ const StringTools = () => {
314314
const outputColor = computeError ? 'text-red-400' : colors.text;
315315

316316
const stats = input
317-
? `${input.length} ký tự · ${new TextEncoder().encode(input).length} bytes`
318-
: 'Chưa có input';
317+
? `${input.length} chars · ${new TextEncoder().encode(input).length} bytes`
318+
: 'No input yet';
319319

320320
return (
321321
<div className="flex flex-col gap-5 h-full">
@@ -349,7 +349,7 @@ const StringTools = () => {
349349
<button
350350
id="upload-file-btn"
351351
onClick={() => fileRef.current?.click()}
352-
title="Tải file text"
352+
title="Load text file"
353353
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium text-slate-400 hover:text-white hover:bg-slate-700 transition-all"
354354
>
355355
<Upload size={13} /> File
@@ -367,7 +367,7 @@ const StringTools = () => {
367367
id="clear-btn"
368368
onClick={handleClear}
369369
disabled={!input}
370-
title="Xoá input"
370+
title="Clear input"
371371
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium text-slate-400 hover:text-red-400 hover:bg-red-500/10 disabled:opacity-30 disabled:cursor-not-allowed transition-all"
372372
>
373373
<Trash2 size={13} /> Clear
@@ -402,7 +402,7 @@ const StringTools = () => {
402402
className={`flex-1 bg-slate-900 border rounded-xl p-4 font-mono text-sm text-slate-200 outline-none resize-none transition-all placeholder-slate-600 focus:border-slate-500 border-slate-700`}
403403
value={input}
404404
onChange={(e) => setInput(e.target.value)}
405-
placeholder={`Nhập ${inputLabel.toLowerCase()} tại đây...`}
405+
placeholder={`Enter ${inputLabel.toLowerCase()} here...`}
406406
/>
407407
</div>
408408

@@ -415,7 +415,7 @@ const StringTools = () => {
415415
</label>
416416
<div className="flex items-center gap-1 shrink-0">
417417
{output && !computeError && (
418-
<span className="text-xs text-slate-600">{output.length} ký tự</span>
418+
<span className="text-xs text-slate-600">{output.length} chars</span>
419419
)}
420420
<CopyButton text={computeError ? '' : output} />
421421
</div>
@@ -426,7 +426,7 @@ const StringTools = () => {
426426
spellCheck={false}
427427
className={`flex-1 bg-slate-800/60 border border-slate-700 rounded-xl p-4 font-mono text-sm outline-none resize-none ${outputColor} ${computeError ? 'border-red-800/50 bg-red-950/20' : ''}`}
428428
value={computeError ? `⚠ ${computeError}` : output}
429-
placeholder="Kết quả sẽ hiển thị ở đây..."
429+
placeholder="Result will appear here..."
430430
/>
431431
</div>
432432
</div>
@@ -494,7 +494,7 @@ const QuickRef = ({ codecId }) => {
494494

495495
return (
496496
<div className="flex flex-col gap-2 shrink-0">
497-
<p className="text-xs font-bold text-slate-600 uppercase tracking-wider">Ví dụ nhanh</p>
497+
<p className="text-xs font-bold text-slate-600 uppercase tracking-wider">Quick examples</p>
498498
<div className="grid grid-cols-1 sm:grid-cols-3 gap-2">
499499
{refs.map((r, i) => (
500500
<div

src/features/T9Converter.jsx

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -148,13 +148,13 @@ const KEYPAD_ROWS = [
148148

149149
// Each example carries both directions so a single click can populate either side.
150150
const EXAMPLES = [
151-
{ label: 'em', latin: 'em', digits: '33 6', note: 'ví dụ trong đề bài' },
152-
{ label: 'hello', latin: 'hello', digits: '44 33 555 555 666', note: 'Nokia cổ điển' },
153-
{ label: 'abc', latin: 'abc', digits: '2 22 222', note: 'lần lượt phím 2' },
154-
{ label: 'hi ok', latin: 'hi ok', digits: '44 444 0 666 5', note: 'space dùng `0` (đứng riêng)' },
155-
{ label: 'can', latin: 'can', digits: '222266', note: 'không quay vòng: 2222 -> ca, +66 -> n' },
156-
{ label: '2024', latin: '2024', digits: '2024', note: 'cụm có `0` -> giữ nguyên số' },
157-
{ label: 'mot ngay', latin: 'mot ngay', digits: '6 666 8 0 66 4 2 999', note: 'chuỗi có dấu cách' },
151+
{ label: 'em', latin: 'em', digits: '33 6', note: 'example from the prompt' },
152+
{ label: 'hello', latin: 'hello', digits: '44 33 555 555 666', note: 'classic Nokia' },
153+
{ label: 'abc', latin: 'abc', digits: '2 22 222', note: 'press 2 repeatedly' },
154+
{ label: 'hi ok', latin: 'hi ok', digits: '44 444 0 666 5', note: 'space uses `0` (alone)' },
155+
{ label: 'can', latin: 'can', digits: '222266', note: 'no wrap: 2222 -> ca, +66 -> n' },
156+
{ label: '2024', latin: '2024', digits: '2024', note: 'cluster with `0` -> kept as number' },
157+
{ label: 'mot ngay', latin: 'mot ngay', digits: '6 666 8 0 66 4 2 999', note: 'string with spaces' },
158158
];
159159

160160
// ─── Component ───────────────────────────────────────────────────────────────
@@ -183,7 +183,7 @@ const T9Converter = () => {
183183
preview={decoded}
184184
previewLabel="Decode (T9 → Latin)"
185185
invalid={t9Invalid}
186-
invalidMsg="Chỉ chấp nhận chữ số 0-9 và khoảng trắng."
186+
invalidMsg="Only digits 0-9 and whitespace are accepted."
187187
showKeypad
188188
/>
189189
<LatinPanel
@@ -192,7 +192,7 @@ const T9Converter = () => {
192192
preview={encoded}
193193
previewLabel="Encode (Latin → T9)"
194194
invalid={latinInvalid}
195-
invalidMsg="Chỉ chấp nhận chữ cái a-z, chữ số 0-9 và khoảng trắng."
195+
invalidMsg="Only letters a-z, digits 0-9, and whitespace are accepted."
196196
/>
197197
</div>
198198

@@ -235,7 +235,7 @@ const PanelShell = ({
235235
icon={copied ? Check : Copy}
236236
className="px-2 py-1"
237237
>
238-
{copied ? 'Đã copy' : 'Copy'}
238+
{copied ? 'Copied' : 'Copy'}
239239
</Button>
240240
)}
241241
{onClear && (
@@ -246,7 +246,7 @@ const PanelShell = ({
246246
icon={Trash2}
247247
className="px-2 py-1"
248248
>
249-
Xóa
249+
Clear
250250
</Button>
251251
)}
252252
</div>
@@ -310,16 +310,16 @@ const DigitPanel = ({ value, onChange, preview, previewLabel, invalid, invalidMs
310310
return (
311311
<div className="flex flex-col gap-3 min-w-0">
312312
<PanelShell
313-
label="Chuỗi phím T9"
313+
label="T9 key sequence"
314314
icon={Hash}
315315
value={value}
316316
onChange={onChange}
317-
placeholder="Ví dụ: 33 6 hoặc 4433555 555666"
317+
placeholder="Example: 33 6 or 4433555 555666"
318318
preview={preview}
319319
previewLabel={previewLabel}
320320
invalid={invalid}
321321
invalidMsg={invalidMsg}
322-
charCount={`${value.length} ký tự`}
322+
charCount={`${value.length} chars`}
323323
onCopy={handleCopy}
324324
onClear={() => onChange('')}
325325
copied={copied}
@@ -333,10 +333,10 @@ const DigitPanel = ({ value, onChange, preview, previewLabel, invalid, invalidMs
333333
)}
334334
{/* Decode cheat-sheet */}
335335
<div className="rounded-xl bg-slate-900/70 border border-slate-700/70 p-3 text-[11px] text-slate-500 leading-relaxed">
336-
<div><span className="font-mono text-slate-300">0</span> đứng riêng (một mình) → space.</div>
337-
<div>Cụm chữ số có chứa <span className="font-mono text-slate-300">0</span> và dài hơn 1 ký tự → giữ nguyên số
338-
(vd <span className="font-mono text-slate-300">2024</span> <span className="font-mono text-blue-300">2024</span>).</div>
339-
<div>Cụm không có <span className="font-mono text-slate-300">0</span> → decode T9 multi-tap.</div>
336+
<div><span className="font-mono text-slate-300">0</span> alone (by itself) -> space.</div>
337+
<div>A digit cluster containing <span className="font-mono text-slate-300">0</span> and longer than 1 character -> kept as a number
338+
(e.g. <span className="font-mono text-slate-300">2024</span> -> <span className="font-mono text-blue-300">2024</span>).</div>
339+
<div>Cluster without <span className="font-mono text-slate-300">0</span> -> T9 multi-tap decode.</div>
340340
</div>
341341
</div>
342342
);
@@ -364,25 +364,25 @@ const LatinPanel = ({ value, onChange, preview, previewLabel, invalid, invalidMs
364364
return (
365365
<div className="flex flex-col gap-3 min-w-0">
366366
<PanelShell
367-
label="Chuỗi Latin"
367+
label="Latin string"
368368
icon={Type}
369369
value={value}
370370
onChange={onChange}
371-
placeholder="Ví dụ: em hoặc hello"
371+
placeholder="Example: em or hello"
372372
preview={preview}
373373
previewLabel={previewLabel}
374374
invalid={invalid}
375375
invalidMsg={invalidMsg}
376-
charCount={`${value.length} ký tự`}
376+
charCount={`${value.length} chars`}
377377
onCopy={handleCopy}
378378
onClear={() => onChange('')}
379379
copied={copied}
380380
/>
381381
{/* Encode cheat-sheet */}
382382
<div className="rounded-xl bg-slate-900/70 border border-slate-700/70 p-3 text-xs text-slate-500 leading-relaxed">
383-
<span className="font-mono text-slate-300">a-z</span>, <span className="font-mono text-slate-300">0-9</span> và dấu cách; ký tự khác bị bỏ qua.
384-
Chữ cái tách bằng khoảng trắng; cụm số giữ nguyên,
385-
ví dụ <span className="font-mono text-slate-300">hello 2024</span> <span className="font-mono text-blue-300">44 33 555 555 666 0 2024</span>.
383+
Type <span className="font-mono text-slate-300">a-z</span>, <span className="font-mono text-slate-300">0-9</span> and whitespace; other characters are dropped.
384+
Letters are space-separated; number clusters pass through,
385+
e.g. <span className="font-mono text-slate-300">hello 2024</span> -> <span className="font-mono text-blue-300">44 33 555 555 666 0 2024</span>.
386386
</div>
387387
</div>
388388
);
@@ -392,7 +392,7 @@ const Keypad = ({ onKey, onSpace, onBackspace }) => (
392392
<div className="flex flex-col gap-2">
393393
<label className="text-xs font-bold text-slate-600 uppercase tracking-wider flex items-center gap-1.5">
394394
<Keyboard size={11} />
395-
Bàn phím ảo
395+
Virtual keypad
396396
</label>
397397
<div className="rounded-xl bg-slate-900/70 border border-slate-700/70 p-3">
398398
<div className="grid grid-cols-3 gap-1.5 max-w-[200px] mx-auto">
@@ -421,14 +421,14 @@ const Keypad = ({ onKey, onSpace, onBackspace }) => (
421421
className="px-2 py-1.5 rounded-md bg-slate-800 hover:bg-slate-700 border border-slate-700 text-slate-300 text-[11px] font-medium flex items-center justify-center gap-1 transition-colors"
422422
>
423423
<Space size={11} />
424-
Tách nhóm
424+
Split group
425425
</button>
426426
<button
427427
type="button"
428428
onClick={onBackspace}
429429
className="px-2 py-1.5 rounded-md bg-slate-800 hover:bg-slate-700 border border-slate-700 text-slate-300 text-[11px] font-medium transition-colors"
430430
>
431-
Xóa 1 ký tự
431+
Delete 1 char
432432
</button>
433433
</div>
434434
</div>
@@ -438,7 +438,7 @@ const Keypad = ({ onKey, onSpace, onBackspace }) => (
438438
const KeyMap = () => (
439439
<div className="flex flex-col gap-2">
440440
<p className="text-xs font-bold text-slate-600 uppercase tracking-wider">
441-
Bảng phím
441+
Key map
442442
</p>
443443
<div className="grid grid-cols-4 gap-2">
444444
{Object.entries(T9_KEYS).map(([digit, letters]) => (
@@ -456,7 +456,7 @@ const KeyMap = () => (
456456
))}
457457
<div className="flex items-center gap-3 px-3 py-2 rounded-lg bg-slate-800/50 border border-slate-700/50 col-span-4">
458458
<span className="font-mono text-base font-bold text-slate-200 w-5 text-center">0</span>
459-
<span className="font-mono text-xs text-slate-400">dấu cách (space)</span>
459+
<span className="font-mono text-xs text-slate-400">space character</span>
460460
</div>
461461
</div>
462462
</div>
@@ -466,7 +466,7 @@ const Examples = ({ onPickT9, onPickLatin }) => (
466466
<div className="flex flex-col gap-2">
467467
<p className="text-xs font-bold text-slate-600 uppercase tracking-wider flex items-center gap-1.5">
468468
<Lightbulb size={11} />
469-
Ví dụ nhanh
469+
Quick examples
470470
</p>
471471
<div className="flex flex-col gap-2">
472472
{EXAMPLES.map((ex) => (
@@ -481,15 +481,15 @@ const Examples = ({ onPickT9, onPickLatin }) => (
481481
<div className="flex items-center gap-1 shrink-0">
482482
<button
483483
type="button"
484-
title={`Nạp "${ex.latin}" vào ô Latin`}
484+
title={`Load "${ex.latin}" into Latin box`}
485485
onClick={() => onPickLatin(ex.latin)}
486486
className="px-2 py-1 rounded text-[10px] font-mono bg-slate-900 hover:bg-slate-700 text-slate-300 border border-slate-700 transition-colors"
487487
>
488488
latin
489489
</button>
490490
<button
491491
type="button"
492-
title={`Nạp "${ex.digits}" vào ô T9`}
492+
title={`Load "${ex.digits}" into T9 box`}
493493
onClick={() => onPickT9(ex.digits)}
494494
className="px-2 py-1 rounded text-[10px] font-mono bg-slate-900 hover:bg-slate-700 text-slate-300 border border-slate-700 transition-colors"
495495
>

0 commit comments

Comments
 (0)