diff --git a/package-lock.json b/package-lock.json
index e418fe9f..31b00869 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -36,6 +36,7 @@
"rehype-raw": "^7.0.0",
"rehype-sanitize": "^6.0.0",
"remark-gfm": "^4.0.1",
+ "smol-toml": "^1.6.1",
"sonner": "^2.0.7",
"sql-formatter": "^15.7.2",
"tailwind-merge": "^3.5.0",
@@ -12513,6 +12514,18 @@
"node": ">= 10"
}
},
+ "node_modules/smol-toml": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz",
+ "integrity": "sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/cyyynthia"
+ }
+ },
"node_modules/sonner": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz",
diff --git a/package.json b/package.json
index 6c29d983..a4d6d940 100644
--- a/package.json
+++ b/package.json
@@ -146,6 +146,7 @@
"rehype-raw": "^7.0.0",
"rehype-sanitize": "^6.0.0",
"remark-gfm": "^4.0.1",
+ "smol-toml": "^1.6.1",
"sonner": "^2.0.7",
"sql-formatter": "^15.7.2",
"tailwind-merge": "^3.5.0",
diff --git a/public/og/tools/de/base-encoding-converter.jpg b/public/og/tools/de/base-encoding-converter.jpg
new file mode 100644
index 00000000..3ebec0b7
Binary files /dev/null and b/public/og/tools/de/base-encoding-converter.jpg differ
diff --git a/public/og/tools/en/base-encoding-converter.jpg b/public/og/tools/en/base-encoding-converter.jpg
new file mode 100644
index 00000000..4a362835
Binary files /dev/null and b/public/og/tools/en/base-encoding-converter.jpg differ
diff --git a/public/og/tools/fr/base-encoding-converter.jpg b/public/og/tools/fr/base-encoding-converter.jpg
new file mode 100644
index 00000000..bee96e90
Binary files /dev/null and b/public/og/tools/fr/base-encoding-converter.jpg differ
diff --git a/public/og/tools/ja/base-encoding-converter.jpg b/public/og/tools/ja/base-encoding-converter.jpg
new file mode 100644
index 00000000..e39a5b80
Binary files /dev/null and b/public/og/tools/ja/base-encoding-converter.jpg differ
diff --git a/public/og/tools/ko/base-encoding-converter.jpg b/public/og/tools/ko/base-encoding-converter.jpg
new file mode 100644
index 00000000..dcd40903
Binary files /dev/null and b/public/og/tools/ko/base-encoding-converter.jpg differ
diff --git a/public/og/tools/zh-CN/base-encoding-converter.jpg b/public/og/tools/zh-CN/base-encoding-converter.jpg
new file mode 100644
index 00000000..b4e66ac3
Binary files /dev/null and b/public/og/tools/zh-CN/base-encoding-converter.jpg differ
diff --git a/public/og/tools/zh-TW/base-encoding-converter.jpg b/public/og/tools/zh-TW/base-encoding-converter.jpg
new file mode 100644
index 00000000..5ee8dde0
Binary files /dev/null and b/public/og/tools/zh-TW/base-encoding-converter.jpg differ
diff --git a/scripts/generators/generate-client-tool-lookup.js b/scripts/generators/generate-client-tool-lookup.js
index a1d343f4..ce6d08c1 100644
--- a/scripts/generators/generate-client-tool-lookup.js
+++ b/scripts/generators/generate-client-tool-lookup.js
@@ -17,6 +17,7 @@ const FAMILY_BY_TOOL_KEY = {
ai_color_palette_generator: "images-media",
asn1_der_inspector: "security-tokens",
barcode_generator: "generators",
+ base_encoding_converter: "encoders-decoders",
base64_encode_decode: "encoders-decoders",
certificate_decoder: "security-tokens",
chmod_calculator: "devops-logs",
diff --git a/src/app/[lang]/base-encoding-converter/layout.tsx b/src/app/[lang]/base-encoding-converter/layout.tsx
new file mode 100644
index 00000000..7fc55621
--- /dev/null
+++ b/src/app/[lang]/base-encoding-converter/layout.tsx
@@ -0,0 +1,30 @@
+import type { Metadata } from "next";
+import { notFound } from "next/navigation";
+import { isValidLocale } from "@/core/i18n/i18n";
+import { buildToolMetadata } from "@/core/seo/seo";
+import { ToolBreadcrumbJsonLd } from "@/core/seo/components/json-ld";
+import { ToolContentTemplateServer } from "@/core/seo/components/tool-content-template-server";
+
+export async function generateMetadata({ params }: { params: Promise<{ lang: string }> }): Promise {
+ const { lang } = await params;
+ if (!isValidLocale(lang)) {
+ notFound();
+ }
+
+ return buildToolMetadata({ lang, slug: "base-encoding-converter" });
+}
+
+export default async function Layout({ children, params }: { children: React.ReactNode; params: Promise<{ lang: string }> }) {
+ const { lang } = await params;
+ if (!isValidLocale(lang)) {
+ notFound();
+ }
+
+ return (
+ <>
+
+ {children}
+
+ >
+ );
+}
diff --git a/src/app/[lang]/base-encoding-converter/page.tsx b/src/app/[lang]/base-encoding-converter/page.tsx
new file mode 100644
index 00000000..5f938a48
--- /dev/null
+++ b/src/app/[lang]/base-encoding-converter/page.tsx
@@ -0,0 +1,7 @@
+"use client"
+
+import { BaseEncodingConverterPage } from "@/features/tools/base-encoding-converter/page"
+
+export default function Page() {
+ return
+}
diff --git a/src/core/i18n/translations/de.json b/src/core/i18n/translations/de.json
index 08f3ca33..d20df3f6 100644
--- a/src/core/i18n/translations/de.json
+++ b/src/core/i18n/translations/de.json
@@ -406,15 +406,16 @@
"error_minify_css": "CSS konnte nicht minimiert werden. Syntax prüfen und erneut versuchen."
},
"yaml_json_converter": {
- "title": "YAML/JSON-Konverter",
- "description": "Konvertieren Sie YAML in JSON und JSON in YAML.",
+ "title": "JSON/YAML/TOML-Konverter",
+ "description": "Konvertieren Sie JSON-, YAML- und TOML-Konfigurationsdaten lokal im Browser.",
"import_file": "Datei importieren",
"convert_action": "Konvertieren",
"drag_drop_import_hint": "Textdatei hierher ziehen oder eine Datei zum Importieren auswählen.",
"import_failed": "Datei konnte nicht importiert werden.",
"copied_desc": "Die konvertierte Ausgabe wurde kopiert.",
"error_yaml_to_json": "YAML konnte nicht in JSON konvertiert werden. Bitte Eingabe prüfen und erneut versuchen.",
- "error_json_to_yaml": "JSON konnte nicht in YAML konvertiert werden. Bitte Eingabe prüfen und erneut versuchen."
+ "error_json_to_yaml": "JSON konnte nicht in YAML konvertiert werden. Bitte Eingabe prüfen und erneut versuchen.",
+ "error_convert": "Die ausgewählten Formate konnten nicht konvertiert werden. Prüfen Sie die Syntax und versuchen Sie es erneut."
},
"markdown_preview": {
"title": "Markdown-Vorschau",
@@ -2540,6 +2541,17 @@
"category": "Kategorie",
"flags": "Markierungen",
"truncated_warning": "Die Eingabe überschreitet das lokale Prüflimit. Zeichenzeilen, Statistiken und kopiertes JSON basieren nur auf dem geprüften Ausschnitt."
+ },
+ "base_encoding_converter": {
+ "title": "Base32/Base58-Konverter",
+ "description": "Kodieren und dekodieren Sie Base32- und Base58-Text lokal, ohne Daten hochzuladen.",
+ "encoding_label": "Kodierung",
+ "operation_label": "Aktion",
+ "encode": "Kodieren",
+ "decode": "Dekodieren",
+ "convert": "Konvertieren",
+ "invalid_base32": "Ungültige Base32-Eingabe. Prüfen Sie Zeichen und Padding.",
+ "invalid_base58": "Ungültige Base58-Eingabe. Base58 schließt 0, O, I und l aus."
}
},
"categories": {
diff --git a/src/core/i18n/translations/en.json b/src/core/i18n/translations/en.json
index 89ee02ac..66c48a2c 100644
--- a/src/core/i18n/translations/en.json
+++ b/src/core/i18n/translations/en.json
@@ -434,15 +434,16 @@
"download_html_success": "Saved HTML file:"
},
"yaml_json_converter": {
- "title": "YAML/JSON Converter",
- "description": "Convert bidirectionally between YAML and JSON seamlessly.",
+ "title": "JSON/YAML/TOML Converter",
+ "description": "Convert JSON, YAML, and TOML configuration data locally in your browser.",
"import_file": "Import File",
"convert_action": "Convert",
"drag_drop_import_hint": "Drag and drop a text file here, or choose a file to import.",
"import_failed": "Unable to import file.",
"copied_desc": "The converted output has been copied.",
"error_yaml_to_json": "Unable to convert YAML to JSON. Check the input and try again.",
- "error_json_to_yaml": "Unable to convert JSON to YAML. Check the input and try again."
+ "error_json_to_yaml": "Unable to convert JSON to YAML. Check the input and try again.",
+ "error_convert": "Unable to convert between the selected formats. Check syntax and try again."
},
"markdown_preview": {
"title": "Markdown Preview",
@@ -2560,6 +2561,17 @@
"category": "Category",
"flags": "Flags",
"truncated_warning": "Input exceeds the local inspection budget. Character rows, stats, and copied JSON are based on the inspected subset."
+ },
+ "base_encoding_converter": {
+ "title": "Base32/Base58 Converter",
+ "description": "Encode and decode Base32 and Base58 text locally without uploading data.",
+ "encoding_label": "Encoding",
+ "operation_label": "Operation",
+ "encode": "Encode",
+ "decode": "Decode",
+ "convert": "Convert",
+ "invalid_base32": "Invalid Base32 input. Check characters and padding.",
+ "invalid_base58": "Invalid Base58 input. Base58 excludes 0, O, I, and l."
}
},
"pages": {
diff --git a/src/core/i18n/translations/fr.json b/src/core/i18n/translations/fr.json
index 29a8a01c..a803a5d4 100644
--- a/src/core/i18n/translations/fr.json
+++ b/src/core/i18n/translations/fr.json
@@ -406,15 +406,16 @@
"error_minify_css": "Impossible de minifier le CSS. Vérifiez la syntaxe puis réessayez."
},
"yaml_json_converter": {
- "title": "Convertisseur YAML/JSON",
- "description": "Convertissez YAML en JSON et JSON en YAML instantanément.",
+ "title": "Convertisseur JSON/YAML/TOML",
+ "description": "Convertissez des données de configuration JSON, YAML et TOML localement dans le navigateur.",
"import_file": "Importer un fichier",
"convert_action": "Convertir",
- "drag_drop_import_hint": "Glissez un fichier texte ici ou choisissez un fichier a importer.",
+ "drag_drop_import_hint": "Glissez un fichier texte ici ou choisissez un fichier à importer.",
"import_failed": "Impossible d'importer le fichier.",
- "copied_desc": "La sortie convertie a ete copiee.",
- "error_yaml_to_json": "Impossible de convertir YAML en JSON. Verifiez l'entree puis reessayez.",
- "error_json_to_yaml": "Impossible de convertir JSON en YAML. Verifiez l'entree puis reessayez."
+ "copied_desc": "La sortie convertie a été copiée.",
+ "error_yaml_to_json": "Impossible de convertir YAML en JSON. Vérifiez l’entrée puis réessayez.",
+ "error_json_to_yaml": "Impossible de convertir JSON en YAML. Vérifiez l’entrée puis réessayez.",
+ "error_convert": "Impossible de convertir entre les formats sélectionnés. Vérifiez la syntaxe puis réessayez."
},
"markdown_preview": {
"title": "Aperçu Markdown",
@@ -2540,6 +2541,17 @@
"category": "Catégorie",
"flags": "Indicateurs",
"truncated_warning": "L'entrée dépasse le budget d'inspection local. Les lignes de caractères, les statistiques et le JSON copié reposent sur le sous-ensemble inspecté."
+ },
+ "base_encoding_converter": {
+ "title": "Convertisseur Base32/Base58",
+ "description": "Encodez et décodez du texte Base32 et Base58 localement, sans téléverser de données.",
+ "encoding_label": "Encodage",
+ "operation_label": "Opération",
+ "encode": "Encoder",
+ "decode": "Décoder",
+ "convert": "Convertir",
+ "invalid_base32": "Entrée Base32 invalide. Vérifiez les caractères et le padding.",
+ "invalid_base58": "Entrée Base58 invalide. Base58 exclut 0, O, I et l."
}
},
"categories": {
diff --git a/src/core/i18n/translations/ja.json b/src/core/i18n/translations/ja.json
index 6b664372..fed5a9c5 100644
--- a/src/core/i18n/translations/ja.json
+++ b/src/core/i18n/translations/ja.json
@@ -406,15 +406,16 @@
"error_minify_css": "CSS を圧縮できませんでした。構文を確認して再試行してください。"
},
"yaml_json_converter": {
- "title": "YAML/JSON 変換",
- "description": "YAML と JSON を相互変換します。",
+ "title": "JSON/YAML/TOML 変換",
+ "description": "JSON、YAML、TOML の設定データをブラウザ内でローカル変換します。",
"import_file": "ファイルを読み込む",
"convert_action": "変換",
"drag_drop_import_hint": "テキストファイルをここにドラッグするか、ファイルを選択して読み込んでください。",
"import_failed": "ファイルを読み込めませんでした。",
"copied_desc": "変換結果をコピーしました。",
"error_yaml_to_json": "YAML を JSON に変換できませんでした。入力を確認して再試行してください。",
- "error_json_to_yaml": "JSON を YAML に変換できませんでした。入力を確認して再試行してください。"
+ "error_json_to_yaml": "JSON を YAML に変換できませんでした。入力を確認して再試行してください。",
+ "error_convert": "選択した形式間で変換できません。構文を確認してもう一度試してください。"
},
"markdown_preview": {
"title": "Markdown プレビュー",
@@ -2540,6 +2541,17 @@
"category": "カテゴリ",
"flags": "フラグ",
"truncated_warning": "入力がローカル検査の上限を超えています。文字行、統計、コピーされる JSON は検査済みの部分だけに基づきます。"
+ },
+ "base_encoding_converter": {
+ "title": "Base32/Base58 変換",
+ "description": "Base32 と Base58 のテキストをアップロードせずにローカルでエンコード、デコードします。",
+ "encoding_label": "エンコード形式",
+ "operation_label": "操作",
+ "encode": "エンコード",
+ "decode": "デコード",
+ "convert": "変換",
+ "invalid_base32": "Base32 入力が無効です。文字とパディングを確認してください。",
+ "invalid_base58": "Base58 入力が無効です。Base58 では 0、O、I、l は使いません。"
}
},
"categories": {
diff --git a/src/core/i18n/translations/ko.json b/src/core/i18n/translations/ko.json
index 6a5e2112..54ed5eb8 100644
--- a/src/core/i18n/translations/ko.json
+++ b/src/core/i18n/translations/ko.json
@@ -406,15 +406,16 @@
"error_minify_css": "CSS 최소화에 실패했습니다. 구문을 확인 후 다시 시도하세요."
},
"yaml_json_converter": {
- "title": "YAML/JSON 변환기",
- "description": "YAML과 JSON을 즉시 상호 변환합니다.",
+ "title": "JSON/YAML/TOML 변환기",
+ "description": "JSON, YAML, TOML 설정 데이터를 브라우저에서 로컬로 변환합니다.",
"import_file": "파일 가져오기",
"convert_action": "변환",
"drag_drop_import_hint": "텍스트 파일을 여기로 끌어다 놓거나 파일을 선택해 가져오세요.",
"import_failed": "파일을 가져올 수 없습니다.",
"copied_desc": "변환 결과를 복사했습니다.",
"error_yaml_to_json": "YAML을 JSON으로 변환할 수 없습니다. 입력을 확인한 뒤 다시 시도하세요.",
- "error_json_to_yaml": "JSON을 YAML로 변환할 수 없습니다. 입력을 확인한 뒤 다시 시도하세요."
+ "error_json_to_yaml": "JSON을 YAML로 변환할 수 없습니다. 입력을 확인한 뒤 다시 시도하세요.",
+ "error_convert": "선택한 형식 간 변환에 실패했습니다. 구문을 확인하고 다시 시도하세요."
},
"markdown_preview": {
"title": "Markdown 미리보기",
@@ -2540,6 +2541,17 @@
"category": "분류",
"flags": "플래그",
"truncated_warning": "입력이 로컬 검사 예산을 초과했습니다. 문자 행, 통계, 복사되는 JSON은 검사된 하위 집합을 기준으로 합니다."
+ },
+ "base_encoding_converter": {
+ "title": "Base32/Base58 변환기",
+ "description": "Base32와 Base58 텍스트를 업로드 없이 로컬에서 인코딩하고 디코딩합니다.",
+ "encoding_label": "인코딩",
+ "operation_label": "작업",
+ "encode": "인코딩",
+ "decode": "디코딩",
+ "convert": "변환",
+ "invalid_base32": "Base32 입력이 올바르지 않습니다. 문자와 패딩을 확인하세요.",
+ "invalid_base58": "Base58 입력이 올바르지 않습니다. Base58은 0, O, I, l을 제외합니다."
}
},
"categories": {
diff --git a/src/core/i18n/translations/zh-CN.json b/src/core/i18n/translations/zh-CN.json
index dd8eb154..590bbd30 100644
--- a/src/core/i18n/translations/zh-CN.json
+++ b/src/core/i18n/translations/zh-CN.json
@@ -406,15 +406,16 @@
"error_minify_css": "CSS 压缩失败,请检查语法后重试。"
},
"yaml_json_converter": {
- "title": "YAML/JSON 转换器",
- "description": "在 YAML 和 JSON 格式之间相互转换。",
+ "title": "JSON/YAML/TOML 转换器",
+ "description": "在浏览器本地转换 JSON、YAML 和 TOML 配置数据。",
"import_file": "导入文件",
"convert_action": "转换",
"drag_drop_import_hint": "将文本文件拖到这里,或选择文件导入。",
"import_failed": "无法导入文件。",
"copied_desc": "转换结果已复制。",
"error_yaml_to_json": "无法将 YAML 转为 JSON。请检查输入后重试。",
- "error_json_to_yaml": "无法将 JSON 转为 YAML。请检查输入后重试。"
+ "error_json_to_yaml": "无法将 JSON 转为 YAML。请检查输入后重试。",
+ "error_convert": "无法在所选格式之间转换。请检查语法后重试。"
},
"markdown_preview": {
"title": "Markdown 预览",
@@ -2540,6 +2541,17 @@
"category": "类别",
"flags": "标记",
"truncated_warning": "输入超过本地检查预算。字符行、统计和复制的 JSON 仅基于已检查的子集。"
+ },
+ "base_encoding_converter": {
+ "title": "Base32/Base58 转换器",
+ "description": "在本地编码和解码 Base32、Base58 文本,无需上传数据。",
+ "encoding_label": "编码",
+ "operation_label": "操作",
+ "encode": "编码",
+ "decode": "解码",
+ "convert": "转换",
+ "invalid_base32": "Base32 输入无效。请检查字符和填充。",
+ "invalid_base58": "Base58 输入无效。Base58 不包含 0、O、I 和 l。"
}
},
"categories": {
diff --git a/src/core/i18n/translations/zh-TW.json b/src/core/i18n/translations/zh-TW.json
index e9085bf5..a2649ed7 100644
--- a/src/core/i18n/translations/zh-TW.json
+++ b/src/core/i18n/translations/zh-TW.json
@@ -406,15 +406,16 @@
"error_minify_css": "CSS 壓縮失敗,請檢查語法後重試。"
},
"yaml_json_converter": {
- "title": "YAML/JSON 轉換器",
- "description": "在 YAML 和 JSON 格式之間互相轉換。",
+ "title": "JSON/YAML/TOML 轉換器",
+ "description": "在瀏覽器本機轉換 JSON、YAML 和 TOML 設定資料。",
"import_file": "匯入檔案",
"convert_action": "轉換",
"drag_drop_import_hint": "將文字檔拖到這裡,或選擇檔案匯入。",
"import_failed": "無法匯入檔案。",
"copied_desc": "轉換結果已複製。",
"error_yaml_to_json": "無法將 YAML 轉為 JSON。請檢查輸入後再試。",
- "error_json_to_yaml": "無法將 JSON 轉為 YAML。請檢查輸入後再試。"
+ "error_json_to_yaml": "無法將 JSON 轉為 YAML。請檢查輸入後再試。",
+ "error_convert": "無法在所選格式之間轉換。請檢查語法後再試。"
},
"markdown_preview": {
"title": "Markdown 預覽",
@@ -2540,6 +2541,17 @@
"category": "類別",
"flags": "標記",
"truncated_warning": "輸入超過本機檢查預算。字元列、統計和複製的 JSON 僅基於已檢查的子集。"
+ },
+ "base_encoding_converter": {
+ "title": "Base32/Base58 轉換器",
+ "description": "在本機編碼與解碼 Base32、Base58 文字,無需上傳資料。",
+ "encoding_label": "編碼",
+ "operation_label": "操作",
+ "encode": "編碼",
+ "decode": "解碼",
+ "convert": "轉換",
+ "invalid_base32": "Base32 輸入無效。請檢查字元與填充。",
+ "invalid_base58": "Base58 輸入無效。Base58 不包含 0、O、I 和 l。"
}
},
"categories": {
diff --git a/src/core/registry/manifests.ts b/src/core/registry/manifests.ts
index 0be37774..954f17b2 100644
--- a/src/core/registry/manifests.ts
+++ b/src/core/registry/manifests.ts
@@ -18,6 +18,7 @@ import { toolManifest as openapiViewerManifest } from "@/features/tools/openapi-
import { toolManifest as jsonDiffViewerManifest } from "@/features/tools/json-diff-viewer/manifest"
import { toolManifest as csvJsonConverterManifest } from "@/features/tools/csv-json-converter/manifest"
import { toolManifest as base64EncodeDecodeManifest } from "@/features/tools/base64-encode-decode/manifest"
+import { toolManifest as baseEncodingConverterManifest } from "@/features/tools/base-encoding-converter/manifest"
import { toolManifest as urlEncodeDecodeManifest } from "@/features/tools/url-encode-decode/manifest"
import { toolManifest as jwtDecoderManifest } from "@/features/tools/jwt-decoder/manifest"
import { toolManifest as jwtWorkbenchManifest } from "@/features/tools/jwt-workbench/manifest"
@@ -142,6 +143,7 @@ export const TOOL_MANIFESTS = [
jsonDiffViewerManifest,
csvJsonConverterManifest,
base64EncodeDecodeManifest,
+ baseEncodingConverterManifest,
urlEncodeDecodeManifest,
jwtDecoderManifest,
jwtWorkbenchManifest,
diff --git a/src/core/registry/menu-groups.ts b/src/core/registry/menu-groups.ts
index 09231d98..0da24520 100644
--- a/src/core/registry/menu-groups.ts
+++ b/src/core/registry/menu-groups.ts
@@ -79,6 +79,7 @@ const PRIMARY_GROUP_BY_FAMILY: Record, PrimaryMe
}
const LEGACY_OVERRIDE_GROUP_BY_TOOL_KEY: Record = {
+ base_encoding_converter: "convert_encode",
base64_encode_decode: "convert_encode",
url_encode_decode: "convert_encode",
image_base64: "convert_encode",
diff --git a/src/core/registry/tool-order.json b/src/core/registry/tool-order.json
index 7800b5dc..6d644f7a 100644
--- a/src/core/registry/tool-order.json
+++ b/src/core/registry/tool-order.json
@@ -19,6 +19,7 @@
"json-diff-viewer",
"csv-json-converter",
"base64-encode-decode",
+ "base-encoding-converter",
"url-encode-decode",
"jwt-decoder",
"jwt-workbench",
diff --git a/src/core/registry/tool-taxonomy.ts b/src/core/registry/tool-taxonomy.ts
index ac93ab99..6aa20d84 100644
--- a/src/core/registry/tool-taxonomy.ts
+++ b/src/core/registry/tool-taxonomy.ts
@@ -58,6 +58,7 @@ const FAMILY_BY_TOOL_KEY: Partial> = {
ai_color_palette_generator: "images-media",
asn1_der_inspector: "security-tokens",
barcode_generator: "generators",
+ base_encoding_converter: "encoders-decoders",
base64_encode_decode: "encoders-decoders",
certificate_decoder: "security-tokens",
chmod_calculator: "devops-logs",
diff --git a/src/core/seo/components/tool-content-template-modules/generated/de.json b/src/core/seo/components/tool-content-template-modules/generated/de.json
index b9fef040..05f6be63 100644
--- a/src/core/seo/components/tool-content-template-modules/generated/de.json
+++ b/src/core/seo/components/tool-content-template-modules/generated/de.json
@@ -10935,5 +10935,114 @@
"Kontrollieren Sie die Darstellung sowohl auf Desktop als auch mobil."
],
"operationalNote": "Unicode-Inspektor sollte als schneller Prüfschritt im Ablauf vor Übergabe, Veröffentlichung und Weitergabe genutzt werden."
+ },
+ "base-encoding-converter": {
+ "content": {
+ "toolKey": "base_encoding_converter",
+ "intro": "Konvertieren Sie Text lokal zwischen Base32 und Base58, um Alphabete, Padding und Roundtrips für Tokens, Kennungen, Testdaten und blockchainbezogene Beispieldaten zuverlässig zu prüfen.",
+ "whatThisToolDoes": [
+ "Es kodiert UTF-8-Text als RFC-4648-Base32 mit Padding, passend für Systeme mit Großbuchstabenalphabet und fester Blockgröße.",
+ "Es dekodiert Base32 zurück zu Text und weist ungültige Zeichen oder falsches Padding zurück, damit Kopier- und Transportfehler sichtbar werden.",
+ "Es unterstützt Bitcoin-artiges Base58, erhält führende Nullbytes und schließt verwechselbare Zeichen wie 0, O, I und l aus.",
+ "Die Konvertierung bleibt im Browser und eignet sich für Kennungen, Wallet-bezogene Beispiele, Bereitstellungscodes und Support-Fixtures ohne API-Upload."
+ ],
+ "useCases": [
+ "Prüfen, ob ein Base32-Recovery-Code oder Bereitstellungswert sauber hin und zurück konvertiert.",
+ "Ein Base58-Kennungsbeispiel beim Debugging von Wallet-, Schlüssel- oder verteilten Systemen dekodieren.",
+ "Kompakte Kompakte Testdaten erstellen, wenn Base64-Zeichen in URLs, Shells oder Dokumentation stören.",
+ "Kontrollieren, ob kopierte Werte durch E-Mail oder Chat Leerzeichen oder verwechselbare Zeichen enthalten.",
+ "Base32, Base58, Base64 und Hex vergleichen, bevor eine Darstellung für einen lokalen Workflow festgelegt wird."
+ ],
+ "inputExamples": [
+ {
+ "label": "Klartext",
+ "value": "byteflow tools"
+ },
+ {
+ "label": "Base32-Eingabe",
+ "value": "MZXW6YTBOI======"
+ },
+ {
+ "label": "Base58-Eingabe",
+ "value": "2NEpo7TZRRrLZSi2U"
+ }
+ ],
+ "outputExamples": [
+ {
+ "label": "Base32-Ausgabe",
+ "value": "MJ4XI2DGN5XWIZLTMF2A===="
+ },
+ {
+ "label": "Base58-Ausgabe",
+ "value": "2NEpo7TZRRrLZSi2U"
+ },
+ {
+ "label": "Prüfhinweis",
+ "value": "Beim Dekodieren von Base58 sollten Werte mit 0, O, I oder l abgelehnt werden."
+ }
+ ],
+ "commonErrors": [
+ {
+ "error": "Base32-Padding fehlt oder steht in der Mitte",
+ "fix": "Verwenden Sie = nur am Ende oder erzeugen Sie den Wert erneut aus dem Quelltext."
+ },
+ {
+ "error": "Base58-Eingabe enthält verwechselbare Zeichen",
+ "fix": "Nutzen Sie einen verifizierten Base58-Wert; das Alphabet schließt 0, O, I und l bewusst aus."
+ },
+ {
+ "error": "Aus E-Mail oder Chat wurden Leerzeichen mitkopiert",
+ "fix": "Entfernen Sie Zeilenumbrüche und Leerzeichen vor dem Dekodieren und führen Sie einen Roundtrip-Test aus."
+ },
+ {
+ "error": "Falsches Alphabet gewählt",
+ "fix": "Wählen Sie Base32 oder Base58 passend zum erzeugenden System, bevor Sie den Inhalt debuggen."
+ },
+ {
+ "error": "Encoding wird als Sicherheit betrachtet",
+ "fix": "Base-Encoding ist umkehrbar; verwenden Sie zusätzlich Verschlüsselung oder Signaturen für Schutz."
+ }
+ ],
+ "privacyNotes": [
+ "Base32- und Base58-Konvertierung laufen lokal im Browser und benötigen keine Netzwerkabfrage.",
+ "Kodierte Werte können weiterhin Geheimnisse offenlegen, weil Encoding umkehrbar ist; maskieren Sie Beispiele vor dem Teilen.",
+ "Leeren Sie gemeinsam genutzte Zwischenablagen nach Debugging oder Dokumentation von Kennungen und Beispielwerten."
+ ],
+ "faqs": [
+ {
+ "q": "Wann sollte ich Base32 statt Base58 wählen?",
+ "a": "Base32 passt, wenn ein System RFC-4648-Ausgabe mit begrenztem Großbuchstabenalphabet und optionalem Padding erwartet."
+ },
+ {
+ "q": "Warum entfernt Base58 manche Zeichen?",
+ "a": "Base58 reduziert visuelle Verwechslungen beim Kopieren von Kennungen und walletbezogenen Werten."
+ },
+ {
+ "q": "Ist Base58 dasselbe wie Base64URL?",
+ "a": "Nein. Base58 verwendet ein anderes Alphabet und Verfahren; Base64URL bleibt Base64 mit URL-sicheren Zeichen."
+ },
+ {
+ "q": "Kann ich damit Produktionsgeheimnisse validieren?",
+ "a": "Sie können die Syntax lokal prüfen, sollten Geheimnisse vor dem Teilen aber rotieren oder maskieren."
+ },
+ {
+ "q": "Wie prüfe ich die Korrektheit?",
+ "a": "Kodieren Sie, dekodieren Sie zurück zu Text und vergleichen Sie mit der ursprünglichen Eingabe, bevor Sie den Wert verwenden."
+ }
+ ]
+ },
+ "workflowSteps": [
+ "Wählen Sie Base32 oder Base58 und bestätigen Sie das Alphabet des Quellsystems.",
+ "Fügen Sie ein Beispiel ein und prüfen Sie zuerst auf ungültige Zeichen oder Paddingfehler.",
+ "Konvertieren Sie die Ausgabe zurück und vergleichen Sie sie mit dem Ursprungstext.",
+ "Entfernen Sie sensible Werte, bevor Sie Dokumentation, Fixtures oder Tickets aktualisieren."
+ ],
+ "qualityChecklist": [
+ "Kodier- und Dekodierrichtung sind korrekt gewählt.",
+ "Base32-Padding mit = steht nur am Ende.",
+ "Base58-Beispiele enthalten keine Zeichen 0, O, I oder l.",
+ "Ausgaben enthalten vor dem Teilen keine echten Tokens, Schlüssel oder personenbezogenen Daten."
+ ],
+ "operationalNote": "Der Base32/Base58-Konverter eignet sich für Kennungsdebugging, Testdaten und Dokumentationsprüfung, ersetzt aber keine Verschlüsselung, Signaturen oder automatisierte Tests."
}
}
diff --git a/src/core/seo/components/tool-content-template-modules/generated/fr.json b/src/core/seo/components/tool-content-template-modules/generated/fr.json
index d55675b9..d80e9d86 100644
--- a/src/core/seo/components/tool-content-template-modules/generated/fr.json
+++ b/src/core/seo/components/tool-content-template-modules/generated/fr.json
@@ -10935,5 +10935,114 @@
"Contrôlez le rendu sur desktop et sur mobile."
],
"operationalNote": "Inspecteur Unicode constitue une étape de vérification rapide avant livraison, publication ou passation."
+ },
+ "base-encoding-converter": {
+ "content": {
+ "toolKey": "base_encoding_converter",
+ "intro": "Convertissez du texte en Base32 et Base58 localement afin de vérifier alphabets, padding et allers-retours pour jetons, identifiants, jeux de test et échantillons proches des usages liés à la blockchain.",
+ "whatThisToolDoes": [
+ "Il encode du texte UTF-8 en Base32 RFC 4648 avec padding, utile pour les systèmes qui attendent un alphabet en majuscules et des blocs stables.",
+ "Il décode la Base32 vers du texte en rejetant les caractères ou paddings invalides qui masqueraient des erreurs de copie ou de transport.",
+ "Il prend en charge la Base58 de style Bitcoin, conserve les octets zéro en tête et exclut les caractères ambigus comme 0, O, I et l.",
+ "La conversion reste dans le navigateur, pratique pour tester identifiants, exemples proches de portefeuilles, codes de configuration et jeux de test de support sans API."
+ ],
+ "useCases": [
+ "Vérifier qu’un code de récupération ou une valeur de configuration Base32 fait un aller-retour propre.",
+ "Décoder un exemple d’identifiant Base58 pendant le débogage d’un portefeuille, d’une clé ou d’un système distribué.",
+ "Créer des jeux de test textuels compacts quand la ponctuation Base64 gêne dans des URL, lignes de commande ou documentations.",
+ "Contrôler qu’une valeur copiée depuis un courriel ou un chat ne contient ni espaces ni caractères ambigus.",
+ "Comparer Base32, Base58, Base64 et hex avant de choisir la représentation d’un flux local."
+ ],
+ "inputExamples": [
+ {
+ "label": "Texte brut",
+ "value": "byteflow tools"
+ },
+ {
+ "label": "Entrée Base32",
+ "value": "MZXW6YTBOI======"
+ },
+ {
+ "label": "Entrée Base58",
+ "value": "2NEpo7TZRRrLZSi2U"
+ }
+ ],
+ "outputExamples": [
+ {
+ "label": "Sortie Base32",
+ "value": "MJ4XI2DGN5XWIZLTMF2A===="
+ },
+ {
+ "label": "Sortie Base58",
+ "value": "2NEpo7TZRRrLZSi2U"
+ },
+ {
+ "label": "Note de validation",
+ "value": "Au décodage Base58, rejetez les valeurs contenant 0, O, I ou l."
+ }
+ ],
+ "commonErrors": [
+ {
+ "error": "Padding Base32 absent ou placé au milieu",
+ "fix": "Utilisez = seulement à la fin, ou régénérez la valeur depuis le texte source."
+ },
+ {
+ "error": "Entrée Base58 avec caractères ambigus",
+ "fix": "Utilisez une valeur Base58 vérifiée ; l’alphabet exclut volontairement 0, O, I et l."
+ },
+ {
+ "error": "Espaces copiés depuis un courriel ou un chat",
+ "fix": "Supprimez retours ligne et espaces avant de décoder, puis faites un aller-retour."
+ },
+ {
+ "error": "Mauvais alphabet sélectionné",
+ "fix": "Choisissez Base32 ou Base58 selon le système producteur avant de déboguer le contenu."
+ },
+ {
+ "error": "Encodage confondu avec sécurité",
+ "fix": "Les encodages Base sont réversibles ; ajoutez chiffrement ou signature pour protéger les données."
+ }
+ ],
+ "privacyNotes": [
+ "Les conversions Base32 et Base58 s’exécutent localement dans le navigateur, sans requête réseau.",
+ "Les valeurs encodées peuvent encore révéler des secrets, car l’encodage est réversible ; masquez les exemples avant partage.",
+ "Nettoyez les identifiants et exemples copiés dans un presse-papiers partagé après le débogage ou la documentation."
+ ],
+ "faqs": [
+ {
+ "q": "Quand choisir Base32 plutôt que Base58 ?",
+ "a": "Choisissez Base32 si un système attend une sortie RFC 4648 avec alphabet majuscule limité et padding optionnel."
+ },
+ {
+ "q": "Pourquoi Base58 exclut certains caractères ?",
+ "a": "Base58 retire les caractères visuellement ambigus pour réduire les erreurs de copie dans les identifiants."
+ },
+ {
+ "q": "Base58 est-il identique à Base64URL ?",
+ "a": "Non. Base58 utilise un alphabet et un processus différents ; Base64URL reste une variante URL-safe de Base64."
+ },
+ {
+ "q": "Puis-je valider des secrets de production ?",
+ "a": "La syntaxe peut être vérifiée localement, mais les secrets doivent être masqués ou renouvelés avant tout partage."
+ },
+ {
+ "q": "Comment confirmer qu’une conversion est correcte ?",
+ "a": "Encodez, décodez vers le texte d’origine, puis comparez avant d’utiliser la valeur dans une doc ou un test."
+ }
+ ]
+ },
+ "workflowSteps": [
+ "Choisissez Base32 ou Base58 et confirmez l’alphabet du système source.",
+ "Collez l’échantillon puis exécutez une conversion pour repérer caractères invalides ou padding incorrect.",
+ "Convertissez la sortie dans le sens inverse et comparez avec le texte d’origine.",
+ "Retirez les valeurs sensibles avant de publier docs, jeux de test ou tickets."
+ ],
+ "qualityChecklist": [
+ "Le sens encodage/décodage est correct.",
+ "Le padding Base32 avec = apparaît uniquement en fin de valeur.",
+ "L’échantillon Base58 ne contient pas 0, O, I ou l.",
+ "La sortie ne contient aucun token, clé ou donnée personnelle réelle avant partage."
+ ],
+ "operationalNote": "Le convertisseur Base32/Base58 convient au débogage d’identifiants, à la préparation de jeux de test et à la revue documentaire, mais ne remplace ni chiffrement, ni signature, ni tests automatisés."
}
}
diff --git a/src/core/seo/components/tool-content-template-modules/generated/ja.json b/src/core/seo/components/tool-content-template-modules/generated/ja.json
index d6c50f78..dd24868e 100644
--- a/src/core/seo/components/tool-content-template-modules/generated/ja.json
+++ b/src/core/seo/components/tool-content-template-modules/generated/ja.json
@@ -12348,5 +12348,114 @@
"デスクトップとモバイルの両方で表示結果を確認します。"
],
"operationalNote": "Unicode インスペクター は、提出・公開・引き継ぎの前に行う迅速な検証ステップとして運用するのが適切です。"
+ },
+ "base-encoding-converter": {
+ "content": {
+ "toolKey": "base_encoding_converter",
+ "intro": "Base32 と Base58 の文字列変換をブラウザ内で行い、トークン、識別子、テスト用データ、ブロックチェーン関連のサンプルのアルファベット、パディング、往復一致を確認できます。",
+ "whatThisToolDoes": [
+ "UTF-8 テキストを RFC 4648 のパディング付き Base32 に変換し、大文字アルファベットと固定ブロックを要求するシステムに合わせやすくします。",
+ "Base32 をテキストへ戻し、不正な文字や不正なパディングを拒否してコピーや転送時の問題を見逃しにくくします。",
+ "Bitcoin 形式の Base58 に対応し、先頭のゼロバイトを保持しつつ 0、O、I、l のような紛らわしい文字を除外します。",
+ "変換はブラウザ内で完結するため、識別子、ウォレット関連のサンプル、設定用コード、サポート用のテストデータ の確認に使えます。"
+ ],
+ "useCases": [
+ "Base32 の復旧コードや設定値が安定して往復変換できるか確認する。",
+ "ウォレット、鍵、分散システムの統合デバッグ中に Base58 識別子サンプルを確認する。",
+ "URL、シェル、ドキュメントで Base64 記号を避けたいテストデータを作る。",
+ "メールやチャットからコピーした値に空白や紛らわしい文字が混入していないか確認する。",
+ "ローカルワークフローで Base32、Base58、Base64、16 進数 のどれを使うべきか比較する。"
+ ],
+ "inputExamples": [
+ {
+ "label": "プレーンテキスト",
+ "value": "byteflow tools"
+ },
+ {
+ "label": "Base32 入力",
+ "value": "MZXW6YTBOI======"
+ },
+ {
+ "label": "Base58 入力",
+ "value": "2NEpo7TZRRrLZSi2U"
+ }
+ ],
+ "outputExamples": [
+ {
+ "label": "Base32 出力",
+ "value": "MJ4XI2DGN5XWIZLTMF2A===="
+ },
+ {
+ "label": "Base58 出力",
+ "value": "2NEpo7TZRRrLZSi2U"
+ },
+ {
+ "label": "検証メモ",
+ "value": "Base58 サンプルのデコード時は、0、O、I、l を含む値を拒否します。"
+ }
+ ],
+ "commonErrors": [
+ {
+ "error": "Base32 のパディングが不足している、または途中にある",
+ "fix": "= は末尾だけに置くか、元のテキストから値を再生成します。"
+ },
+ {
+ "error": "Base58 入力に紛らわしい文字が含まれる",
+ "fix": "確認済みの Base58 値を使ってください。このアルファベットでは 0、O、I、l を意図的に除外します。"
+ },
+ {
+ "error": "メールやチャットからコピーした空白が混入している",
+ "fix": "改行や空白を取り除いてからデコードし、往復確認を行います。"
+ },
+ {
+ "error": "違うアルファベットを選んでいる",
+ "fix": "生成元システムに合わせて Base32 または Base58 を切り替えてから内容を調べます。"
+ },
+ {
+ "error": "エンコードを保護だと考えている",
+ "fix": "Base エンコードは可逆です。保護には暗号化や署名を別途使ってください。"
+ }
+ ],
+ "privacyNotes": [
+ "Base32 と Base58 の変換はブラウザ内で実行され、ネットワーク要求は不要です。",
+ "エンコード値も秘密を含む可能性があります。共有前に認証情報や個人情報を必ずマスクしてください。",
+ "デバッグや文書作成後は、共有クリップボードに残った識別子やサンプル値を消してください。"
+ ],
+ "faqs": [
+ {
+ "q": "Base32 はいつ選ぶべきですか?",
+ "a": "RFC 4648 の出力、限定された大文字アルファベット、任意のパディングを要求するシステムでは Base32 が適しています。"
+ },
+ {
+ "q": "Base58 が一部の文字を除外する理由は?",
+ "a": "識別子やウォレット風の値を手入力、コピーするときの視覚的な誤読を減らすためです。"
+ },
+ {
+ "q": "Base58 は Base64URL と同じですか?",
+ "a": "いいえ。Base58 は異なるアルファベットと変換方法を使います。Base64URL は URL 安全な Base64 です。"
+ },
+ {
+ "q": "本番の秘密値を検証できますか?",
+ "a": "構文確認はローカルでできますが、共有前に秘密値はローテーションまたはマスクしてください。"
+ },
+ {
+ "q": "変換が正しいことはどう確認しますか?",
+ "a": "エンコード後にデコードして元のテキストと一致するか比較してから、文書やテストで使ってください。"
+ }
+ ]
+ },
+ "workflowSteps": [
+ "Base32 または Base58 を選び、生成元システムのアルファベットを確認します。",
+ "サンプルを貼り付けて一度変換し、不正文字やパディングエラーを確認します。",
+ "出力を逆方向に変換し、元のテキストと一致することを確認します。",
+ "文書、テスト用データ、チケットに貼る前に機密値を取り除きます。"
+ ],
+ "qualityChecklist": [
+ "エンコードとデコードの向きを取り違えていないか確認します。",
+ "Base32 の = パディングが末尾だけにあるか確認します。",
+ "Base58 サンプルに 0、O、I、l が含まれていないか確認します。",
+ "共有前に実際のトークン、鍵、個人情報が残っていないか確認します。"
+ ],
+ "operationalNote": "Base32/Base58 変換は、識別子デバッグ、テスト用データ作成、文書レビューに向いていますが、暗号化、署名、正式な自動化テストの代替にはなりません。"
}
}
diff --git a/src/core/seo/components/tool-content-template-modules/generated/ko.json b/src/core/seo/components/tool-content-template-modules/generated/ko.json
index 13b9e971..5129322a 100644
--- a/src/core/seo/components/tool-content-template-modules/generated/ko.json
+++ b/src/core/seo/components/tool-content-template-modules/generated/ko.json
@@ -10935,5 +10935,114 @@
"데스크톱과 모바일 뷰포트 모두에서 표시 상태를 확인합니다."
],
"operationalNote": "Unicode 인스펙터 은 배포, 공유, 인수인계 전에 실행하는 빠른 검증 단계로 운영하는 것이 좋습니다."
+ },
+ "base-encoding-converter": {
+ "content": {
+ "toolKey": "base_encoding_converter",
+ "intro": "Base32와 Base58 텍스트 변환을 브라우저에서 로컬로 수행해 토큰, 식별자, 테스트 데이터, 블록체인 관련 예시의 알파벳, 패딩, 왕복 일치 여부를 확인합니다.",
+ "whatThisToolDoes": [
+ "UTF-8 텍스트를 패딩이 있는 RFC 4648 Base32로 인코딩해 대문자 알파벳과 고정 블록을 요구하는 시스템과 맞추기 쉽습니다.",
+ "Base32를 텍스트로 되돌리면서 잘못된 문자나 패딩을 거부해 복사와 전송 오류를 조기에 발견합니다.",
+ "Bitcoin 스타일 Base58을 지원하며 선행 0 바이트를 보존하고 0, O, I, l처럼 혼동하기 쉬운 문자를 제외합니다.",
+ "모든 변환은 브라우저 안에서 끝나므로 식별자, 지갑 관련 샘플, 설정 코드, 지원용 테스트 데이터를 API 없이 확인할 수 있습니다."
+ ],
+ "useCases": [
+ "Base32 복구 코드나 설정 값이 안정적으로 왕복 변환되는지 확인합니다.",
+ "지갑, 키, 분산 시스템 통합 디버깅 중 Base58 식별자 샘플을 확인합니다.",
+ "URL, 셸, 문서에서 Base64 문장부호가 불편한 테스트 데이터를 만듭니다.",
+ "메일이나 채팅에서 복사한 인코딩 값에 공백이나 혼동 문자가 섞이지 않았는지 확인합니다.",
+ "로컬 워크플로에 적합한 표현을 고를 때 Base32, Base58, Base64, 16진수 출력을 비교합니다."
+ ],
+ "inputExamples": [
+ {
+ "label": "일반 텍스트",
+ "value": "byteflow tools"
+ },
+ {
+ "label": "Base32 입력",
+ "value": "MZXW6YTBOI======"
+ },
+ {
+ "label": "Base58 입력",
+ "value": "2NEpo7TZRRrLZSi2U"
+ }
+ ],
+ "outputExamples": [
+ {
+ "label": "Base32 출력",
+ "value": "MJ4XI2DGN5XWIZLTMF2A===="
+ },
+ {
+ "label": "Base58 출력",
+ "value": "2NEpo7TZRRrLZSi2U"
+ },
+ {
+ "label": "검증 메모",
+ "value": "Base58 샘플을 디코딩할 때 0, O, I, l이 포함된 값은 거부해야 합니다."
+ }
+ ],
+ "commonErrors": [
+ {
+ "error": "Base32 패딩이 없거나 중간에 있습니다",
+ "fix": "= 패딩은 끝에만 두거나 원본 텍스트에서 값을 다시 생성하세요."
+ },
+ {
+ "error": "Base58 입력에 혼동 문자가 포함됩니다",
+ "fix": "확인된 Base58 값을 사용하세요. 이 알파벳은 0, O, I, l을 의도적으로 제외합니다."
+ },
+ {
+ "error": "메일이나 채팅에서 복사한 공백이 섞였습니다",
+ "fix": "줄바꿈과 공백을 제거한 뒤 디코딩하고 왕복 검사를 수행하세요."
+ },
+ {
+ "error": "잘못된 알파벳을 선택했습니다",
+ "fix": "생성 시스템에 맞게 Base32 또는 Base58을 먼저 선택한 뒤 내용를 디버깅하세요."
+ },
+ {
+ "error": "인코딩을 보안 보호로 착각했습니다",
+ "fix": "Base32/Base58 인코딩은 되돌릴 수 있습니다. 보호에는 별도의 암호화나 서명이 필요합니다."
+ }
+ ],
+ "privacyNotes": [
+ "Base32와 Base58 변환은 브라우저에서 로컬로 실행되며 네트워크 요청이 필요 없습니다.",
+ "인코딩된 값도 비밀을 노출할 수 있으므로 예시를 공유하기 전에 반드시 마스킹하세요.",
+ "디버깅이나 문서 작업이 끝나면 공유 클립보드에 남은 식별자와 샘플 값을 정리하세요."
+ ],
+ "faqs": [
+ {
+ "q": "언제 Base32를 선택해야 하나요?",
+ "a": "RFC 4648 출력, 제한된 대문자 알파벳, 선택적 패딩을 요구하는 시스템에는 Base32가 적합합니다."
+ },
+ {
+ "q": "Base58이 일부 문자와 숫자를 제외하는 이유는 무엇인가요?",
+ "a": "식별자나 지갑 형태 값을 복사할 때 시각적으로 헷갈리는 문자를 줄이기 위해서입니다."
+ },
+ {
+ "q": "Base58은 Base64URL과 같은가요?",
+ "a": "아닙니다. Base58은 다른 알파벳과 변환 방식을 사용하며 Base64URL은 URL 안전 Base64입니다."
+ },
+ {
+ "q": "운영 비밀 값을 검증할 수 있나요?",
+ "a": "문법은 로컬에서 확인할 수 있지만 공유 전에는 비밀 값을 회전하거나 마스킹해야 합니다."
+ },
+ {
+ "q": "변환이 맞는지 어떻게 확인하나요?",
+ "a": "인코딩한 뒤 다시 디코딩해 원본 텍스트와 비교한 후 문서나 테스트에 사용하세요."
+ }
+ ]
+ },
+ "workflowSteps": [
+ "Base32 또는 Base58을 선택하고 원본 시스템의 알파벳을 확인합니다.",
+ "샘플을 붙여 넣고 한 번 변환해 잘못된 문자나 패딩 오류를 확인합니다.",
+ "출력을 반대 방향으로 변환해 원본 텍스트와 일치하는지 확인합니다.",
+ "문서, 테스트 데이터, 티켓에 넣기 전에 민감한 값을 제거합니다."
+ ],
+ "qualityChecklist": [
+ "인코딩과 디코딩 방향을 반대로 선택하지 않았는지 확인합니다.",
+ "Base32의 = 패딩이 끝에만 있는지 확인합니다.",
+ "Base58 샘플에 0, O, I, l이 없는지 확인합니다.",
+ "공유 전 실제 토큰, 키, 개인정보가 출력에 없는지 확인합니다."
+ ],
+ "operationalNote": "Base32/Base58 변환기는 식별자 디버깅, 테스트 데이터 준비, 문서 검토에 적합하지만 암호화, 서명, 정식 자동화 테스트를 대체하지 않습니다."
}
}
diff --git a/src/core/seo/components/tool-content-template-modules/generated/zh-CN.json b/src/core/seo/components/tool-content-template-modules/generated/zh-CN.json
index cb1fcd31..c44262dc 100644
--- a/src/core/seo/components/tool-content-template-modules/generated/zh-CN.json
+++ b/src/core/seo/components/tool-content-template-modules/generated/zh-CN.json
@@ -12332,5 +12332,114 @@
"在桌面和移动视口都检查展示效果。"
],
"operationalNote": "Unicode 检查器 应作为交付流程中的快速校验步骤,在提交、发布和交接前都建议执行一次。"
+ },
+ "base-encoding-converter": {
+ "content": {
+ "toolKey": "base_encoding_converter",
+ "intro": "在浏览器本地完成 Base32 和 Base58 文本转换,适合检查令牌、标识符、测试样例和区块链相关示例的字母表、填充和往返一致性。",
+ "whatThisToolDoes": [
+ "它把 UTF-8 文本编码为带填充的 RFC 4648 Base32,方便与要求大写字母表和固定分组的系统对接。",
+ "它可以把 Base32 解回文本,并拒绝错误字符或错误填充,避免复制和传输问题被忽略。",
+ "它支持 Bitcoin 风格 Base58,保留前导零字节,并排除 0、O、I、l 等容易混淆的字符。",
+ "所有转换都在浏览器本地完成,适合调试标识符、钱包样本、配置代码和支持工单附件。"
+ ],
+ "useCases": [
+ "检查 Base32 恢复码或配置值是否可以稳定往返。",
+ "在钱包、密钥或分布式系统集成调试时解码 Base58 标识符样本。",
+ "为测试、文档或命令行示例生成不含 Base64 标点的紧凑文本样例。",
+ "确认从邮件或聊天复制的编码值没有混入空白或易混字符。",
+ "在选择本地工作流表示形式时,对比 Base32、Base58、Base64 和十六进制输出。"
+ ],
+ "inputExamples": [
+ {
+ "label": "纯文本",
+ "value": "byteflow tools"
+ },
+ {
+ "label": "Base32 输入",
+ "value": "MZXW6YTBOI======"
+ },
+ {
+ "label": "Base58 输入",
+ "value": "2NEpo7TZRRrLZSi2U"
+ }
+ ],
+ "outputExamples": [
+ {
+ "label": "Base32 输出",
+ "value": "MJ4XI2DGN5XWIZLTMF2A===="
+ },
+ {
+ "label": "Base58 输出",
+ "value": "2NEpo7TZRRrLZSi2U"
+ },
+ {
+ "label": "校验说明",
+ "value": "解码 Base58 样本时,应拒绝包含 0、O、I 或 l 的值。"
+ }
+ ],
+ "commonErrors": [
+ {
+ "error": "Base32 填充缺失或出现在中间",
+ "fix": "只在末尾使用 = 填充,或从源文本重新生成编码值。"
+ },
+ {
+ "error": "Base58 输入包含易混字符",
+ "fix": "改用经过确认的 Base58 值;该字母表会有意排除 0、O、I 和 l。"
+ },
+ {
+ "error": "从邮件或聊天复制时混入空白",
+ "fix": "先去掉换行和空格,再执行解码和往返检查。"
+ },
+ {
+ "error": "选择了错误的字母表",
+ "fix": "先切换 Base32 或 Base58 以匹配生成端系统,再继续排查内容。"
+ },
+ {
+ "error": "把编码当成安全保护",
+ "fix": "Base32/Base58 编码可逆;保密仍需要单独的加密或签名机制。"
+ }
+ ],
+ "privacyNotes": [
+ "Base32 和 Base58 转换都在浏览器本地运行,不需要网络请求。",
+ "编码后的值仍可能暴露秘密,因为编码是可逆的,分享示例前请先脱敏。",
+ "调试或写文档结束后,请清理共享剪贴板中的标识符和样本值。"
+ ],
+ "faqs": [
+ {
+ "q": "什么时候应该用 Base32?",
+ "a": "当系统要求 RFC 4648 输出、有限的大写字母表和可选填充时,优先使用 Base32。"
+ },
+ {
+ "q": "为什么 Base58 会排除一些字母和数字?",
+ "a": "Base58 会移除视觉上容易混淆的字符,以减少人工复制标识符时的错误。"
+ },
+ {
+ "q": "Base58 和 Base64URL 一样吗?",
+ "a": "不一样。Base58 使用不同字母表和转换过程;Base64URL 仍然是 URL 安全版本的 Base64。"
+ },
+ {
+ "q": "这个工具能验证生产秘密吗?",
+ "a": "它可以本地检查语法,但编码后的秘密在分享前仍应轮换或脱敏。"
+ },
+ {
+ "q": "如何确认转换结果正确?",
+ "a": "先编码,再解码回文本,并与原始输入逐字比较后再用于文档或测试。"
+ }
+ ]
+ },
+ "workflowSteps": [
+ "选择 Base32 或 Base58,并确认来源系统使用的字母表。",
+ "粘贴样本后先执行一次转换,检查是否有非法字符或填充错误。",
+ "把输出再反向转换回原始文本,确认往返一致。",
+ "在写入文档、测试样例或工单前移除敏感值。"
+ ],
+ "qualityChecklist": [
+ "确认编码和解码方向没有选反。",
+ "检查 Base32 的 = 填充只出现在末尾。",
+ "确认 Base58 样本不包含 0、O、I 和 l。",
+ "分享前确认输出不包含真实令牌、密钥或个人信息。"
+ ],
+ "operationalNote": "Base32/Base58 转换器适合放在标识符调试、测试样例准备和文档复核环节,不能替代加密、签名或正规的自动化测试。"
}
}
diff --git a/src/core/seo/components/tool-content-template-modules/generated/zh-TW.json b/src/core/seo/components/tool-content-template-modules/generated/zh-TW.json
index 7525d6f8..1c338417 100644
--- a/src/core/seo/components/tool-content-template-modules/generated/zh-TW.json
+++ b/src/core/seo/components/tool-content-template-modules/generated/zh-TW.json
@@ -10931,5 +10931,114 @@
"在桌面與行動視口都檢查呈現效果。"
],
"operationalNote": "Unicode 檢查器 應作為交付流程中的快速驗證步驟,建議在提交、發布與交接前執行一次。"
+ },
+ "base-encoding-converter": {
+ "content": {
+ "toolKey": "base_encoding_converter",
+ "intro": "在瀏覽器本機完成 Base32 與 Base58 文字轉換,適合檢查權杖、識別碼、測試樣本與區塊鏈相關範例的字母表、填充與往返一致性。",
+ "whatThisToolDoes": [
+ "它會把 UTF-8 文字編碼為帶填充的 RFC 4648 Base32,方便與要求大寫字母表和固定分組的系統對接。",
+ "它可以把 Base32 解回文字,並拒絕錯誤字元或錯誤填充,避免複製和傳輸問題被忽略。",
+ "它支援 Bitcoin 風格 Base58,保留前導零位元組,並排除 0、O、I、l 等容易混淆的字元。",
+ "所有轉換都在瀏覽器本機完成,適合除錯識別碼、錢包樣本、設定代碼和支援工單附件。"
+ ],
+ "useCases": [
+ "檢查 Base32 復原碼或設定值是否可以穩定往返。",
+ "在錢包、金鑰或分散式系統整合除錯時解碼 Base58 識別碼樣本。",
+ "為測試、文件或命令列範例產生不含 Base64 標點的緊湊文字測試樣本。",
+ "確認從郵件或聊天複製的編碼值沒有混入空白或易混字元。",
+ "在選擇本機工作流程表示形式時,比較 Base32、Base58、Base64 和十六進位輸出。"
+ ],
+ "inputExamples": [
+ {
+ "label": "純文字",
+ "value": "byteflow tools"
+ },
+ {
+ "label": "Base32 輸入",
+ "value": "MZXW6YTBOI======"
+ },
+ {
+ "label": "Base58 輸入",
+ "value": "2NEpo7TZRRrLZSi2U"
+ }
+ ],
+ "outputExamples": [
+ {
+ "label": "Base32 輸出",
+ "value": "MJ4XI2DGN5XWIZLTMF2A===="
+ },
+ {
+ "label": "Base58 輸出",
+ "value": "2NEpo7TZRRrLZSi2U"
+ },
+ {
+ "label": "驗證說明",
+ "value": "解碼 Base58 樣本時,應拒絕包含 0、O、I 或 l 的值。"
+ }
+ ],
+ "commonErrors": [
+ {
+ "error": "Base32 填充缺失或出現在中間",
+ "fix": "只在結尾使用 = 填充,或從來源文字重新產生編碼值。"
+ },
+ {
+ "error": "Base58 輸入包含易混字元",
+ "fix": "改用經過確認的 Base58 值;該字母表會刻意排除 0、O、I 和 l。"
+ },
+ {
+ "error": "從郵件或聊天複製時混入空白",
+ "fix": "先去掉換行和空格,再執行解碼與往返檢查。"
+ },
+ {
+ "error": "選錯字母表",
+ "fix": "先切換 Base32 或 Base58 以匹配產生端系統,再繼續排查內容。"
+ },
+ {
+ "error": "把編碼當成安全保護",
+ "fix": "Base32/Base58 編碼可逆;保密仍需要另外的加密或簽章機制。"
+ }
+ ],
+ "privacyNotes": [
+ "Base32 和 Base58 轉換都在瀏覽器本機執行,不需要網路請求。",
+ "編碼後的值仍可能暴露秘密,因為編碼是可逆的,分享範例前請先脫敏。",
+ "除錯或撰寫文件結束後,請清理共享剪貼簿中的識別碼和樣本值。"
+ ],
+ "faqs": [
+ {
+ "q": "什麼時候應該用 Base32?",
+ "a": "當系統要求 RFC 4648 輸出、有限的大寫字母表和可選填充時,優先使用 Base32。"
+ },
+ {
+ "q": "為什麼 Base58 會排除一些字母和數字?",
+ "a": "Base58 會移除視覺上容易混淆的字元,以減少人工複製識別碼時的錯誤。"
+ },
+ {
+ "q": "Base58 和 Base64URL 一樣嗎?",
+ "a": "不一樣。Base58 使用不同字母表和轉換過程;Base64URL 仍然是 URL 安全版本的 Base64。"
+ },
+ {
+ "q": "這個工具能驗證生產秘密嗎?",
+ "a": "它可以本機檢查語法,但編碼後的秘密在分享前仍應輪換或脫敏。"
+ },
+ {
+ "q": "如何確認轉換結果正確?",
+ "a": "先編碼,再解碼回文字,並與原始輸入逐字比較後再用於文件或測試。"
+ }
+ ]
+ },
+ "workflowSteps": [
+ "選擇 Base32 或 Base58,並確認來源系統使用的字母表。",
+ "貼上樣本後先執行一次轉換,檢查是否有非法字元或填充錯誤。",
+ "把輸出再反向轉換回原始文字,確認往返一致。",
+ "寫入文件、測試樣本 或工單前移除敏感值。"
+ ],
+ "qualityChecklist": [
+ "確認編碼和解碼方向沒有選反。",
+ "檢查 Base32 的 = 填充只出現在結尾。",
+ "確認 Base58 樣本不包含 0、O、I 和 l。",
+ "分享前確認輸出不包含真實 權杖、金鑰或個人資訊。"
+ ],
+ "operationalNote": "Base32/Base58 轉換器適合放在識別碼除錯、測試樣本準備和文件複核環節,不能取代加密、簽章或正規的自動化測試。"
}
}
diff --git a/src/core/seo/components/tool-content-template-modules/top-templates.ts b/src/core/seo/components/tool-content-template-modules/top-templates.ts
index b1ab40d5..053d6ee1 100644
--- a/src/core/seo/components/tool-content-template-modules/top-templates.ts
+++ b/src/core/seo/components/tool-content-template-modules/top-templates.ts
@@ -93,6 +93,52 @@ export const TOP_TOOL_CONTENT_TEMPLATES: Record
{ q: "How do I verify correctness for files?", a: "Decode the output and compare hashes of original and restored files to confirm byte-level parity." },
],
},
+ "base-encoding-converter": {
+ toolKey: "base_encoding_converter",
+ intro: "Convert text through Base32 and Base58 locally, with predictable alphabets, padding behavior, and round-trip checks for tokens, identifiers, fixtures, and blockchain-adjacent data that must stay readable across systems.",
+ whatThisToolDoes: [
+ "It encodes UTF-8 text into RFC 4648 Base32 with padding so output works with systems that expect fixed alphabet and block sizing.",
+ "It decodes Base32 back to text while rejecting malformed characters and padding shapes that would hide copy or transport errors.",
+ "It encodes and decodes Bitcoin-style Base58, preserving leading zero bytes and excluding ambiguous characters such as 0, O, I, and l.",
+ "It keeps the conversion in the browser, which is useful when testing identifiers, wallet-adjacent samples, provisioning codes, and support fixtures without sending data to an API.",
+ ],
+ useCases: [
+ "Check whether a Base32 recovery code or provisioning value round-trips cleanly before documenting it.",
+ "Decode a Base58 identifier sample during wallet, key, or distributed-system integration debugging.",
+ "Create compact text fixtures for tests where Base64 punctuation would be awkward in URLs, shells, or docs.",
+ "Verify that copied encoded strings do not contain ambiguous characters or whitespace introduced by email or chat tools.",
+ "Compare Base32, Base58, Base64, and hex outputs when choosing the safest representation for a local workflow.",
+ ],
+ inputExamples: [
+ { label: "Plain text", value: "byteflow tools" },
+ { label: "Base32 input", value: "MZXW6YTBOI======" },
+ { label: "Base58 input", value: "2NEpo7TZRRrLZSi2U" },
+ ],
+ outputExamples: [
+ { label: "Base32 output", value: "MJ4XI2DGN5XWIZLTMF2A====" },
+ { label: "Base58 output", value: "2NEpo7TZRRrLZSi2U" },
+ { label: "Validation note", value: "Reject values containing 0, O, I, or l when decoding Base58 samples." },
+ ],
+ commonErrors: [
+ { error: "Base32 padding is missing or placed in the middle", fix: "Use '=' padding only at the end, or regenerate the value from the source text." },
+ { error: "Base58 input contains ambiguous characters", fix: "Replace the source with a verified Base58 value; the alphabet intentionally excludes 0, O, I, and l." },
+ { error: "Whitespace copied from email or chat", fix: "Trim line breaks and spaces before decoding, then run a round-trip check." },
+ { error: "Wrong alphabet selected", fix: "Switch between Base32 and Base58 to match the producing system before debugging the payload itself." },
+ { error: "Encoding treated as security", fix: "Remember that Base encodings are reversible; use encryption or signing separately for protection." },
+ ],
+ privacyNotes: [
+ "Base32 and Base58 conversions run locally in the browser and do not require network requests.",
+ "Encoded values can still expose secrets because encoding is reversible, so mask credentials before sharing examples.",
+ "Clear copied identifiers from shared clipboards after finishing debugging or documentation work.",
+ ],
+ faqs: [
+ { q: "When should I choose Base32 over Base58?", a: "Use Base32 when a system expects RFC 4648 output with a limited uppercase alphabet and optional padding." },
+ { q: "Why does Base58 exclude some letters and numbers?", a: "Base58 removes visually ambiguous characters to reduce copy mistakes in identifiers and wallet-style values." },
+ { q: "Is Base58 the same as Base64URL?", a: "No. Base58 uses a different alphabet and conversion process; Base64URL is still Base64 with URL-safe characters." },
+ { q: "Can this validate production secrets?", a: "It can check syntax locally, but encoded secrets should still be rotated or masked before sharing." },
+ { q: "How should I confirm a conversion is correct?", a: "Encode, decode back to text, and compare the result with the original input before using the value in docs or tests." },
+ ],
+ },
"jwt-decoder": {
toolKey: "jwt_decoder",
intro: "Decode JWT headers and payload claims to inspect token structure during authentication and authorization debugging with a repeatable, privacy-first review workflow that helps teams isolate claim issues before escalating to signature, key-distribution, or policy-layer analysis.",
diff --git a/src/features/tools/base-encoding-converter/manifest.ts b/src/features/tools/base-encoding-converter/manifest.ts
new file mode 100644
index 00000000..1bd991bf
--- /dev/null
+++ b/src/features/tools/base-encoding-converter/manifest.ts
@@ -0,0 +1,10 @@
+import type { ToolMeta } from "@/core/registry/types"
+
+export const toolManifest = {
+ key: "base_encoding_converter",
+ slug: "base-encoding-converter",
+ category: "text-string",
+ relatedTools: ["base64_encode_decode", "hex_bytes_workbench", "url_encode_decode", "hash_generator"],
+ keywords: ["base32 encoder", "base32 decoder", "base58 encoder", "base58 decoder", "base encoding converter"],
+ searchKeywords: ["base32", "base58", "bitcoin base58", "encoding converter", "base encode", "base decode", "编码", "解码"],
+} satisfies ToolMeta
diff --git a/src/features/tools/base-encoding-converter/page.tsx b/src/features/tools/base-encoding-converter/page.tsx
new file mode 100644
index 00000000..e89418f3
--- /dev/null
+++ b/src/features/tools/base-encoding-converter/page.tsx
@@ -0,0 +1,153 @@
+"use client"
+
+import * as React from "react"
+import { Binary, Copy, RotateCcw, TestTube2 } from "lucide-react"
+import { toast } from "sonner"
+import { Button } from "@/components/ui/button"
+import { Textarea } from "@/components/ui/textarea"
+import { useLang } from "@/core/i18n/lang-provider"
+import { safeClipboardWrite } from "@/core/clipboard/clipboard"
+import { convertBaseEncoding, type BaseEncoding } from "./utils"
+
+type Operation = "encode" | "decode"
+
+const SAMPLE_BY_ENCODING: Record = {
+ base32: { plain: "byteflow tools", encoded: "MJ4XI2DGN5XWIZLTMF2A====" },
+ base58: { plain: "Hello World!", encoded: "2NEpo7TZRRrLZSi2U" },
+}
+
+export function BaseEncodingConverterPage() {
+ const { t } = useLang()
+ const toolT = t.tools["base_encoding_converter"] as Record
+ const text = React.useCallback((key: string) => toolT[key], [toolT])
+ const [encoding, setEncoding] = React.useState("base32")
+ const [operation, setOperation] = React.useState("encode")
+ const [input, setInput] = React.useState(SAMPLE_BY_ENCODING.base32.plain)
+ const [output, setOutput] = React.useState("")
+ const [error, setError] = React.useState(null)
+
+ const run = React.useCallback(() => {
+ try {
+ setOutput(convertBaseEncoding(input, encoding, operation))
+ setError(null)
+ } catch {
+ setOutput("")
+ setError(encoding === "base32" ? text("invalid_base32") : text("invalid_base58"))
+ }
+ }, [encoding, input, operation, text])
+
+ const useSample = () => {
+ const sample = SAMPLE_BY_ENCODING[encoding]
+ setInput(operation === "encode" ? sample.plain : sample.encoded)
+ setOutput("")
+ setError(null)
+ }
+
+ const reset = () => {
+ setInput("")
+ setOutput("")
+ setError(null)
+ }
+
+ const copyOutput = async () => {
+ if (!output) return
+ const result = await safeClipboardWrite(output)
+ if (!result.ok) {
+ toast.error(t.common.copy_failed)
+ return
+ }
+ toast.success(t.common.copied, { description: t.common.copied_desc })
+ }
+
+ return (
+
+
+
+
+ {toolT.title}
+
+
{toolT.description}
+
+
+
+
+
+
+ {(["base32", "base58"] as BaseEncoding[]).map((item) => (
+
+ ))}
+
+
+
+
+
+ {(["encode", "decode"] as Operation[]).map((item) => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+ {error ?
{error}
: null}
+
+
+
+ )
+}
diff --git a/src/features/tools/base-encoding-converter/utils.ts b/src/features/tools/base-encoding-converter/utils.ts
new file mode 100644
index 00000000..ae1ee607
--- /dev/null
+++ b/src/features/tools/base-encoding-converter/utils.ts
@@ -0,0 +1,151 @@
+export type BaseEncoding = "base32" | "base58"
+
+const BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
+const BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
+const textEncoder = new TextEncoder()
+const textDecoder = new TextDecoder()
+
+function bytesToText(bytes: Uint8Array): string {
+ return textDecoder.decode(bytes)
+}
+
+function textToBytes(value: string): Uint8Array {
+ return textEncoder.encode(value)
+}
+
+export function encodeBytesToBase32(bytes: Uint8Array): string {
+ if (bytes.length === 0) return ""
+
+ let output = ""
+ let buffer = 0
+ let bitsLeft = 0
+
+ for (const byte of bytes) {
+ buffer = (buffer << 8) | byte
+ bitsLeft += 8
+
+ while (bitsLeft >= 5) {
+ output += BASE32_ALPHABET[(buffer >>> (bitsLeft - 5)) & 31]
+ bitsLeft -= 5
+ }
+ }
+
+ if (bitsLeft > 0) {
+ output += BASE32_ALPHABET[(buffer << (5 - bitsLeft)) & 31]
+ }
+
+ while (output.length % 8 !== 0) {
+ output += "="
+ }
+
+ return output
+}
+
+export function decodeBase32ToBytes(value: string): Uint8Array {
+ const normalized = value.trim().replace(/\s+/g, "").toUpperCase()
+ if (!normalized) return new Uint8Array()
+ if (!/^[A-Z2-7]+=*$/.test(normalized) || /=[^=]/.test(normalized)) {
+ throw new Error("Invalid Base32 character.")
+ }
+
+ const unpadded = normalized.replace(/=+$/g, "")
+ const bytes: number[] = []
+ let buffer = 0
+ let bitsLeft = 0
+
+ for (const char of unpadded) {
+ const valueIndex = BASE32_ALPHABET.indexOf(char)
+ if (valueIndex < 0) {
+ throw new Error("Invalid Base32 character.")
+ }
+
+ buffer = (buffer << 5) | valueIndex
+ bitsLeft += 5
+
+ if (bitsLeft >= 8) {
+ bytes.push((buffer >>> (bitsLeft - 8)) & 255)
+ bitsLeft -= 8
+ }
+ }
+
+ return new Uint8Array(bytes)
+}
+
+export function encodeBytesToBase58(bytes: Uint8Array): string {
+ if (bytes.length === 0) return ""
+
+ const digits = [0]
+ for (const byte of bytes) {
+ let carry = byte
+ for (let index = 0; index < digits.length; index += 1) {
+ carry += digits[index] << 8
+ digits[index] = carry % BASE58_ALPHABET.length
+ carry = Math.floor(carry / BASE58_ALPHABET.length)
+ }
+ while (carry > 0) {
+ digits.push(carry % BASE58_ALPHABET.length)
+ carry = Math.floor(carry / BASE58_ALPHABET.length)
+ }
+ }
+
+ for (const byte of bytes) {
+ if (byte !== 0) break
+ digits.push(0)
+ }
+
+ return digits.reverse().map((digit) => BASE58_ALPHABET[digit]).join("")
+}
+
+export function decodeBase58ToBytes(value: string): Uint8Array {
+ const normalized = value.trim().replace(/\s+/g, "")
+ if (!normalized) return new Uint8Array()
+
+ const bytes = [0]
+ for (const char of normalized) {
+ const digit = BASE58_ALPHABET.indexOf(char)
+ if (digit < 0) {
+ throw new Error("Invalid Base58 character.")
+ }
+ let carry = digit
+ for (let index = 0; index < bytes.length; index += 1) {
+ carry += bytes[index] * BASE58_ALPHABET.length
+ bytes[index] = carry & 255
+ carry >>= 8
+ }
+ while (carry > 0) {
+ bytes.push(carry & 255)
+ carry >>= 8
+ }
+ }
+
+ for (const char of normalized) {
+ if (char !== BASE58_ALPHABET[0]) break
+ bytes.push(0)
+ }
+
+ return new Uint8Array(bytes.reverse())
+}
+
+export function encodeTextToBase32(value: string): string {
+ return encodeBytesToBase32(textToBytes(value))
+}
+
+export function decodeBase32ToText(value: string): string {
+ return bytesToText(decodeBase32ToBytes(value))
+}
+
+export function encodeTextToBase58(value: string): string {
+ return encodeBytesToBase58(textToBytes(value))
+}
+
+export function decodeBase58ToText(value: string): string {
+ return bytesToText(decodeBase58ToBytes(value))
+}
+
+export function convertBaseEncoding(input: string, encoding: BaseEncoding, operation: "encode" | "decode"): string {
+ if (encoding === "base32") {
+ return operation === "encode" ? encodeTextToBase32(input) : decodeBase32ToText(input)
+ }
+
+ return operation === "encode" ? encodeTextToBase58(input) : decodeBase58ToText(input)
+}
diff --git a/src/features/tools/yaml-json-converter/manifest.ts b/src/features/tools/yaml-json-converter/manifest.ts
index f00a18b8..6e80280f 100644
--- a/src/features/tools/yaml-json-converter/manifest.ts
+++ b/src/features/tools/yaml-json-converter/manifest.ts
@@ -4,7 +4,7 @@ export const toolManifest = {
key: "yaml_json_converter",
slug: "yaml-json-converter",
category: "formatters",
- relatedTools: ["json_formatter", "json_to_typescript", "jsonpath_playground", "xml_formatter"],
- keywords: ["yaml to json", "json to yaml", "yaml converter online", "yaml json transform"],
- searchKeywords: ["convert yaml", "yaml parser", "json converter", "YAML转换", "YAML変換", "YAML 변환", "配置转换"],
+ relatedTools: ["json_formatter", "json_to_typescript", "structured_data_visualizer", "xml_formatter"],
+ keywords: ["yaml to json", "json to yaml", "toml to json", "json to toml", "yaml json toml converter"],
+ searchKeywords: ["convert yaml", "convert toml", "toml parser", "json converter", "YAML转换", "TOML转换", "YAML変換", "TOML変換", "YAML 변환", "TOML 변환", "配置转换"],
} satisfies ToolMeta
diff --git a/src/features/tools/yaml-json-converter/page.tsx b/src/features/tools/yaml-json-converter/page.tsx
index d4a6149b..44d4b292 100644
--- a/src/features/tools/yaml-json-converter/page.tsx
+++ b/src/features/tools/yaml-json-converter/page.tsx
@@ -12,10 +12,17 @@ import { readStorageString, removeStorageKey, writeStorageString } from "@/core/
import { buildToolHandoffLink } from "@/core/routing/tool-handoff"
import { importTextFile, TEXT_FILE_IMPORT_ACCEPT } from "@/core/files/text-file-import"
import { safeClipboardWrite } from "@/core/clipboard/clipboard"
-import { convertYamlJson, type YamlJsonMode } from "./utils"
+import { convertStructuredData, type StructuredDataFormat } from "./utils"
const INPUT_STORAGE_KEY = "byteflow:yaml-json-converter:input"
const MODE_STORAGE_KEY = "byteflow:yaml-json-converter:mode"
+const FROM_FORMAT_STORAGE_KEY = "byteflow:yaml-json-converter:from-format"
+const TO_FORMAT_STORAGE_KEY = "byteflow:yaml-json-converter:to-format"
+const FORMAT_OPTIONS: StructuredDataFormat[] = ["yaml", "json", "toml"]
+
+function monacoLanguage(format: StructuredDataFormat) {
+ return format === "toml" ? "ini" : format
+}
export function YamlJsonConverterPage() {
const { t, lang } = useLang()
@@ -23,7 +30,8 @@ export function YamlJsonConverterPage() {
const text = React.useCallback((key: string) => toolT[key], [toolT])
const [input, setInput] = React.useState("")
const [output, setOutput] = React.useState("")
- const [mode, setMode] = React.useState("yaml-to-json")
+ const [fromFormat, setFromFormat] = React.useState("yaml")
+ const [toFormat, setToFormat] = React.useState("json")
const [error, setError] = React.useState(null)
const [importError, setImportError] = React.useState(null)
const [isImportDragActive, setIsImportDragActive] = React.useState(false)
@@ -37,9 +45,23 @@ export function YamlJsonConverterPage() {
setInput(savedInput)
}
+ const savedFromFormat = readStorageString(FROM_FORMAT_STORAGE_KEY)
+ const savedToFormat = readStorageString(TO_FORMAT_STORAGE_KEY)
+ if (savedFromFormat && FORMAT_OPTIONS.includes(savedFromFormat as StructuredDataFormat)) {
+ setFromFormat(savedFromFormat as StructuredDataFormat)
+ }
+ if (savedToFormat && FORMAT_OPTIONS.includes(savedToFormat as StructuredDataFormat)) {
+ setToFormat(savedToFormat as StructuredDataFormat)
+ return
+ }
+
const savedMode = readStorageString(MODE_STORAGE_KEY)
- if (savedMode === "yaml-to-json" || savedMode === "json-to-yaml") {
- setMode(savedMode)
+ if (savedMode === "yaml-to-json") {
+ setFromFormat("yaml")
+ setToFormat("json")
+ } else if (savedMode === "json-to-yaml") {
+ setFromFormat("json")
+ setToFormat("yaml")
}
}, [])
@@ -49,12 +71,13 @@ export function YamlJsonConverterPage() {
}, [input])
React.useEffect(() => {
- writeStorageString(MODE_STORAGE_KEY, mode)
- }, [mode])
+ writeStorageString(FROM_FORMAT_STORAGE_KEY, fromFormat)
+ writeStorageString(TO_FORMAT_STORAGE_KEY, toFormat)
+ }, [fromFormat, toFormat])
- const toggleMode = () => {
- setMode(prev => prev === "yaml-to-json" ? "json-to-yaml" : "yaml-to-json")
- // Optionally swap input/output contents
+ const swapFormats = () => {
+ setFromFormat(toFormat)
+ setToFormat(fromFormat)
const temp = input
setInput(output)
setOutput(temp)
@@ -70,12 +93,12 @@ export function YamlJsonConverterPage() {
}
try {
- setOutput(convertYamlJson(input, mode))
+ setOutput(convertStructuredData(input, { from: fromFormat, to: toFormat }))
setError(null)
} catch {
- setError(mode === "yaml-to-json" ? text("error_yaml_to_json") : text("error_json_to_yaml"))
+ setError(text("error_convert"))
}
- }, [input, mode, text])
+ }, [fromFormat, input, text, toFormat])
const openImportPicker = () => {
fileInputRef.current?.click()
@@ -134,9 +157,9 @@ export function YamlJsonConverterPage() {
return () => window.removeEventListener("keydown", handleKeyDown)
}, [output, handleCopy, doConvert])
- const inputLang = mode === "yaml-to-json" ? "yaml" : "json"
- const outputLang = mode === "yaml-to-json" ? "json" : "yaml"
- const jsonFormatterHandoffPayload = mode === "yaml-to-json" ? output : ""
+ const inputLang = monacoLanguage(fromFormat)
+ const outputLang = monacoLanguage(toFormat)
+ const jsonFormatterHandoffPayload = toFormat === "json" ? output : ""
const jsonFormatterHandoff = React.useMemo(
() => buildToolHandoffLink(lang, "json-formatter", jsonFormatterHandoffPayload),
[jsonFormatterHandoffPayload, lang],
@@ -161,11 +184,47 @@ export function YamlJsonConverterPage() {
-