Develop (#55)
feat: optimize dashboard/search UX and simplify extension architecture Redesign Dashboard with compact tool grid and recently used tools Improve TopBar search UX with Cmd/Ctrl+K shortcut and better history navigation Reorganize project structure into src/ Migrate i18n from react-i18next to chrome.i18n Remove runtime language switch and settings page Remove HTML/Markdown conversion tools Clean up unused code, dead animations, redundant comments, and imports Improve component consistency with shadcn/ui patterns Replace hardcoded strings/colors with i18n tokens and theme tokens Add comprehensive project documentation and coding standards Fix CI artifact upload workflow and multiple TypeScript/test issues Includes various refactors, UI polish, i18n cleanup, CI improvements, and maintenance updates across the codebase.
This commit is contained in:
-144
@@ -1,144 +0,0 @@
|
||||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import type { CustomDetector } from 'i18next-browser-languagedetector'; // 💡 1. 引入官方强类型探测器接口
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
import { storageUtil } from '@/utils/chromeStorage';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
// 导入 Day.js 本地化语言包
|
||||
import 'dayjs/locale/zh-cn';
|
||||
|
||||
// 同步加载全局核心命名空间
|
||||
import commonZh from './locales/zh/common.json';
|
||||
import featuresZh from './locales/zh/features.json';
|
||||
import commonEn from './locales/en/common.json';
|
||||
import featuresEn from './locales/en/features.json';
|
||||
|
||||
const resources = {
|
||||
zh: {
|
||||
common: commonZh,
|
||||
features: featuresZh,
|
||||
},
|
||||
en: {
|
||||
common: commonEn,
|
||||
features: featuresEn,
|
||||
},
|
||||
};
|
||||
|
||||
export const SUPPORTED_LANGUAGES = ['zh', 'en'] as const;
|
||||
export type SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number];
|
||||
|
||||
const LANGUAGE_STORAGE_KEY = 'app/language';
|
||||
const LANGUAGE_SNAPSHOT_KEY = 'snapshot/app/language';
|
||||
|
||||
/**
|
||||
* 将任意语言标识归一化为受支持的核心代码
|
||||
*/
|
||||
export const normalizeLanguage = (lng: string): SupportedLanguage => {
|
||||
if (!lng) return 'en';
|
||||
return lng.toLowerCase().startsWith('zh') ? 'zh' : 'en';
|
||||
};
|
||||
|
||||
/**
|
||||
* 严格校验语言安全边界
|
||||
*/
|
||||
const isValidLanguage = (lng: unknown): lng is SupportedLanguage => {
|
||||
return typeof lng === 'string' && (SUPPORTED_LANGUAGES as readonly string[]).includes(lng);
|
||||
};
|
||||
|
||||
/**
|
||||
* 同步从 localStorage 获取语言快照(消除异步闪烁)
|
||||
*/
|
||||
const getSyncLanguageSnapshot = (): SupportedLanguage | null => {
|
||||
try {
|
||||
const val = localStorage.getItem(LANGUAGE_SNAPSHOT_KEY);
|
||||
if (!val) return null;
|
||||
const parsed = JSON.parse(val) as unknown;
|
||||
return isValidLanguage(parsed) ? parsed : null;
|
||||
} catch (error) {
|
||||
console.error('[i18n] Failed to parse sync language snapshot from localStorage:', error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// 💡 2. 强类型接口重塑:显式绑定 CustomDetector 类型,
|
||||
// 告诉 TS 编译器这些方法将被全局 Languagedetector 框架隐式调用,彻底治愈“未使用函数”报错!
|
||||
const chromeStorageDetector: CustomDetector = {
|
||||
name: 'chromeStorage',
|
||||
lookup() {
|
||||
return undefined;
|
||||
},
|
||||
cacheUserLanguage(lng: string) {
|
||||
const target = normalizeLanguage(lng);
|
||||
// 💡 修复点:对异步写盘操作追加 void 算子或 catch,吞掉 Promise 被忽略警告
|
||||
storageUtil.set(LANGUAGE_STORAGE_KEY, target).catch((err) => {
|
||||
console.error('[i18n Detector Error] Failed to write back language state:', err);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
const detector = new LanguageDetector();
|
||||
detector.addDetector(chromeStorageDetector);
|
||||
|
||||
const syncLng = getSyncLanguageSnapshot();
|
||||
|
||||
// 💡 3. 修复点:对 i18n.init() 返回的异步 Promise 前方追加 void 斩断依赖链,放行编译
|
||||
void i18n
|
||||
.use(detector)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
resources,
|
||||
fallbackLng: 'en',
|
||||
lng: syncLng || undefined,
|
||||
ns: ['common', 'features'],
|
||||
defaultNS: 'common',
|
||||
debug: false,
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
detection: {
|
||||
order: ['chromeStorage', 'navigator'],
|
||||
caches: ['chromeStorage'],
|
||||
},
|
||||
});
|
||||
|
||||
// 监听语言变更
|
||||
i18n.on('languageChanged', (lng) => {
|
||||
const normalizedLng = normalizeLanguage(lng);
|
||||
dayjs.locale(normalizedLng === 'zh' ? 'zh-cn' : 'en');
|
||||
localStorage.setItem(LANGUAGE_SNAPSHOT_KEY, JSON.stringify(normalizedLng));
|
||||
});
|
||||
|
||||
// 初始化时从长期异步存储中恢复校准
|
||||
storageUtil
|
||||
.get(LANGUAGE_STORAGE_KEY)
|
||||
.then((lng) => {
|
||||
const rawTargetLng = lng || syncLng;
|
||||
|
||||
if (!rawTargetLng) {
|
||||
const initialLng = normalizeLanguage(i18n.language);
|
||||
|
||||
// 💡 修复点:对初始化同步写盘追加安全的 Promise .catch() 异常隔离防护罩
|
||||
storageUtil.set(LANGUAGE_STORAGE_KEY, initialLng).catch((err) => {
|
||||
console.error('[i18n Init Error] Persistent sync collapsed:', err);
|
||||
});
|
||||
|
||||
if (initialLng !== normalizeLanguage(i18n.language)) {
|
||||
// 💡 修复点:对 changeLanguage 异步微任务进行显式 void 断链安全隔离
|
||||
void i18n.changeLanguage(initialLng);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const targetLng = normalizeLanguage(String(rawTargetLng));
|
||||
|
||||
if (isValidLanguage(targetLng) && targetLng !== normalizeLanguage(i18n.language)) {
|
||||
// 💡 修复点:对 changeLanguage 异步微任务进行显式 void 断链安全隔离
|
||||
void i18n.changeLanguage(targetLng);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('[i18n Context Error] Async local storage lookup collapsed:', err);
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
@@ -1,37 +0,0 @@
|
||||
{
|
||||
"pageTitle": "Base64 Converter",
|
||||
"pageSubtitle": "Encode and decode text, files, and images with Base64",
|
||||
"textMode": "Text",
|
||||
"fileMode": "File",
|
||||
"imageMode": "Image",
|
||||
"encode": "Encode",
|
||||
"decode": "Decode",
|
||||
"clear": "Clear",
|
||||
"textInputPlaceholder": "Enter text to encode to Base64...",
|
||||
"base64InputPlaceholder": "Enter Base64 string to decode...",
|
||||
"base64Output": "Base64 Output",
|
||||
"textOutput": "Decoded Text Output",
|
||||
"copyRaw": "Copy Raw Base64",
|
||||
"copyDataUri": "Copy Data URI",
|
||||
"clickOrDropToFile": "Click or drop a file here",
|
||||
"clickOrDropToImage": "Click or drop an image here",
|
||||
"clickOrDropToReplace": "Click or drop to replace the file",
|
||||
"maxFileSize": "Maximum file size: {{max}}",
|
||||
"supportedFormats": "Supports PNG, JPG, WEBP, GIF, BMP, SVG, etc.",
|
||||
"fileSizeExceeded": "File size exceeds the limit (max {{max}})",
|
||||
"unsupportedImageType": "Unsupported image format",
|
||||
"conversionFailed": "Conversion failed",
|
||||
"originalSize": "Original Size",
|
||||
"encodedSize": "Encoded Size",
|
||||
"invalidBase64": "Invalid Base64 string",
|
||||
"binaryDataDetected": "Input appears to be binary data (e.g. an image). Please switch to the Image tab.",
|
||||
"imageDataUriHint": "Detected an image data URI — please use the Image tab to decode it.",
|
||||
"switchToImageMode": "Switch to Image mode",
|
||||
"download": "Download",
|
||||
"decodedFileName": "Decoded file name",
|
||||
"decodeBase64Placeholder": "Enter Base64 or data URI to decode...",
|
||||
"decodedFileOutput": "Decoded File",
|
||||
"decodedImageOutput": "Decoded Image",
|
||||
"inferredMimeType": "Inferred MIME type",
|
||||
"decodedSize": "Decoded size"
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
{
|
||||
"appName": "Testing Tools",
|
||||
"buttons": {
|
||||
"save": "Save",
|
||||
"cancel": "Cancel",
|
||||
"confirm": "Confirm",
|
||||
"copy": "Copy",
|
||||
"clear": "Clear",
|
||||
"refresh": "Refresh",
|
||||
"toggleLanguage": "Switch Language",
|
||||
"toggleTheme": "Toggle theme",
|
||||
"themeMode": {
|
||||
"light": "Switch to dark mode",
|
||||
"dark": "Switch to system mode",
|
||||
"system": "Switch to light mode"
|
||||
},
|
||||
"search": "Search tools...",
|
||||
"back": "Back",
|
||||
"clearSearch": "Clear search",
|
||||
"recentSearch": "Recent search",
|
||||
"noResults": "No tools found",
|
||||
"openInTab": "Open in tab",
|
||||
"settings": "Settings"
|
||||
},
|
||||
"messages": {
|
||||
"copySuccess": "Copied to clipboard",
|
||||
"copyError": "Copy failed",
|
||||
"copyEmpty": "Nothing to copy"
|
||||
},
|
||||
"errorBoundary": {
|
||||
"title": "Oops, something went wrong",
|
||||
"description": "The app encountered an unexpected error. You can try refreshing the page or resetting the app.",
|
||||
"refresh": "Refresh App",
|
||||
"retry": "Retry"
|
||||
},
|
||||
"pageErrorBoundary": {
|
||||
"title": "This feature has encountered an error",
|
||||
"description": "An internal script error occurred while loading or rendering this page. You can try again or switch to another tool from the navigation menu."
|
||||
},
|
||||
"router": {
|
||||
"notFound": "Page Not Found",
|
||||
"notFoundDescription": "This feature is not available or has been removed in the current runtime ({{entryPointType}})."
|
||||
},
|
||||
"textInputArea": {
|
||||
"clear": "Clear",
|
||||
"copyContent": "Copy content",
|
||||
"cleared": "Cleared",
|
||||
"placeholder": "placeholder text"
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
{
|
||||
"dashboard": {
|
||||
"title": "Dashboard"
|
||||
},
|
||||
"timestamp": {
|
||||
"title": "Timestamp",
|
||||
"description": "Unix millisecond conversion and formatting"
|
||||
},
|
||||
"storageCleaner": {
|
||||
"title": "Storage Cleaner",
|
||||
"description": "Clean cache, cookies, and local storage"
|
||||
},
|
||||
"qrCode": {
|
||||
"title": "QR Code Tools",
|
||||
"description": "Generate QR code for the selected URL"
|
||||
},
|
||||
"textStatistics": {
|
||||
"title": "Text Statistics",
|
||||
"description": "Real-time analysis of text characters, words, and bytes"
|
||||
},
|
||||
"jwt": {
|
||||
"title": "JWT Parser",
|
||||
"description": "JSON Web Token decoding and viewing"
|
||||
},
|
||||
"jsonDiff": {
|
||||
"title": "JSON Tools",
|
||||
"description": "Diff, format, YAML/TOML conversion, and minify"
|
||||
},
|
||||
"base64Converter": {
|
||||
"title": "Base64 Converter",
|
||||
"description": "Encode text, files, and images to Base64"
|
||||
},
|
||||
"markdownToHtml": {
|
||||
"title": "Markdown to HTML",
|
||||
"description": "Real-time Markdown conversion and HTML preview"
|
||||
},
|
||||
"htmlToMarkdown": {
|
||||
"title": "HTML to Markdown",
|
||||
"description": "Real-time HTML conversion and Markdown preview"
|
||||
},
|
||||
"rightClickRestorer": {
|
||||
"title": "Right Click Restorer",
|
||||
"description": "Detect and restore disabled browser right-click menus"
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"pageTitle": "HTML to Markdown",
|
||||
"pageSubtitle": "Convert HTML to Markdown in real-time",
|
||||
"splitMode": "Split",
|
||||
"previewMode": "Preview",
|
||||
"markdownMode": "Markdown",
|
||||
"clear": "Clear",
|
||||
"inputLabel": "HTML Input",
|
||||
"previewLabel": "Markdown Preview",
|
||||
"markdownOutputLabel": "Markdown Output",
|
||||
"inputPlaceholder": "Enter your HTML content here...",
|
||||
"charCount": "{{count}} chars",
|
||||
"copyMarkdown": "Copy Markdown",
|
||||
"download": "Download",
|
||||
"emptyHint": "Enter HTML content to see the conversion result"
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"pageTitle": "JSON Diff",
|
||||
"pageSubtitle": "Compare differences between two JSON values",
|
||||
"leftPlaceholder": "Enter the original JSON...",
|
||||
"rightPlaceholder": "Enter the target JSON...",
|
||||
"leftLabel": "Original JSON",
|
||||
"rightLabel": "Target JSON",
|
||||
"compareButton": "Compare",
|
||||
"clearButton": "Clear",
|
||||
"sideBySideMode": "Side by side",
|
||||
"unifiedMode": "Unified",
|
||||
"previousDiff": "Previous",
|
||||
"nextDiff": "Next",
|
||||
"noDiffs": "No differences",
|
||||
"diffCount": "{{count}} differences",
|
||||
"invalidJson": "Invalid JSON format",
|
||||
"emptyHint": "Enter JSON on both sides and click Compare",
|
||||
"fixErrorHint": "Fix the JSON syntax errors above to enable live comparison",
|
||||
"added": "Added",
|
||||
"removed": "Removed",
|
||||
"modified": "Modified"
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
{
|
||||
"formatTitle": "JSON Formatter",
|
||||
"formatSubtitle": "Beautify and format JSON data",
|
||||
"inputPlaceholder": "Enter JSON to format...",
|
||||
"formatButton": "Format",
|
||||
"clearButton": "Clear",
|
||||
"sortKeys": "Sort Keys",
|
||||
"sortKeysTooltip": "Sort JSON object keys in alphabetical order",
|
||||
"indentSize": "Indent",
|
||||
"indentSpaces": "{{count}} spaces",
|
||||
"outputLabel": "Formatted Result",
|
||||
"copySuccess": "Copied",
|
||||
"copyFail": "Copy failed",
|
||||
"noContent": "Nothing to copy",
|
||||
"invalidJson": "Invalid JSON format",
|
||||
"emptyHint": "Enter JSON and click Format",
|
||||
"fixErrorHint": "Fix the JSON syntax errors above to enable live formatting",
|
||||
"originalSize": "Original size",
|
||||
"formattedSize": "Formatted size",
|
||||
"diffMode": "Diff",
|
||||
"formatMode": "Format",
|
||||
"yamlMode": "YAML",
|
||||
"tomlMode": "TOML",
|
||||
"minifyMode": "Minify",
|
||||
"yamlTitle": "JSON to YAML",
|
||||
"yamlSubtitle": "Convert JSON data to YAML format",
|
||||
"yamlModeInputPlaceholder": "Enter JSON to convert...",
|
||||
"yamlModeOutputLabel": "YAML Result",
|
||||
"yamlModeEmptyHint": "Enter JSON and click Convert",
|
||||
"convertButton": "Convert",
|
||||
"tomlTitle": "JSON to TOML",
|
||||
"tomlSubtitle": "Convert JSON data to TOML format",
|
||||
"tomlModeInputPlaceholder": "Enter JSON to convert...",
|
||||
"tomlModeOutputLabel": "TOML Result",
|
||||
"tomlModeEmptyHint": "Enter JSON and click Convert",
|
||||
"minifyTitle": "JSON Minifier",
|
||||
"minifySubtitle": "Compress JSON into a compact single-line format",
|
||||
"minifyModeInputPlaceholder": "Enter JSON to minify...",
|
||||
"minifyModeOutputLabel": "Minified Result",
|
||||
"minifyModeEmptyHint": "Enter JSON and click Minify",
|
||||
"minifyButton": "Minify"
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"pageTitle": "JWT Parser",
|
||||
"pageSubtitle": "JSON Web Token decoding and viewing",
|
||||
"placeholder": "Paste Encoded JWT here...",
|
||||
"headerTitle": "HEADER: Algorithm & Token Type",
|
||||
"payloadTitle": "PAYLOAD: Data",
|
||||
"signatureTitle": "Signature",
|
||||
"noSignature": "No Signature",
|
||||
"invalidFormat": "Unable to parse",
|
||||
"errors": {
|
||||
"invalidBase64String": "Invalid base64url string",
|
||||
"failedToDecode": "Failed to decode base64url: ",
|
||||
"invalidFormat": "Invalid JWT format: expected 3 parts separated by .",
|
||||
"parseHeaderFailed": "Failed to parse Header: ",
|
||||
"parsePayloadFailed": "Failed to parse Payload: "
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"pageTitle": "Markdown to HTML",
|
||||
"pageSubtitle": "Convert Markdown to HTML in real-time with preview",
|
||||
"splitMode": "Split",
|
||||
"previewMode": "Preview",
|
||||
"htmlMode": "HTML",
|
||||
"clear": "Clear",
|
||||
"print": "Print",
|
||||
"download": "Download",
|
||||
"inputLabel": "Markdown Input",
|
||||
"previewLabel": "Live Preview",
|
||||
"htmlOutputLabel": "HTML Output",
|
||||
"inputPlaceholder": "Enter your Markdown content here...",
|
||||
"charCount": "{{count}} chars",
|
||||
"copyHtml": "Copy HTML",
|
||||
"downloadSuccess": "File downloaded successfully",
|
||||
"printSuccess": "Print window opened"
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
{
|
||||
"pageTitle": "QR Code Tools",
|
||||
"pageSubtitle": "Generate and parse QR codes",
|
||||
|
||||
"generateMode": "Generate QR Code",
|
||||
"parseMode": "Parse QR Code",
|
||||
|
||||
"urlToQr": "Text to QR Code",
|
||||
"qrToUrl": "QR Code to Text",
|
||||
|
||||
"urlInputLabel": "Enter URL or Text",
|
||||
"urlInputPlaceholder": "Enter URL or text content, QR code will be generated automatically",
|
||||
"generateButton": "Generate QR Code",
|
||||
"generating": "Generating...",
|
||||
|
||||
"qrCodeWillShow": "QR code will be shown here",
|
||||
"downloadButton": "Download QR Code",
|
||||
"copyQrButton": "Copy QR Code Image",
|
||||
|
||||
"qrCodeSuccess": "QR code generated successfully",
|
||||
"qrCodeDownloadSuccess": "QR code downloaded successfully",
|
||||
"qrCodeCopySuccess": "QR code copied to clipboard",
|
||||
|
||||
"selectImage": "Please select a QR code image",
|
||||
"parseSuccess": "QR code parsed successfully",
|
||||
"noQrDetected": "No QR code detected, please ensure the image is clear and contains a QR code",
|
||||
"parseError": "Failed to parse QR code, please try again",
|
||||
"generateError": "Failed to generate QR code, please check the input",
|
||||
"copyError": "Copy failed, please try again",
|
||||
|
||||
"imagePasted": "Image pasted, parsing...",
|
||||
"imagePasteError": "Failed to paste image, please try again",
|
||||
"imageCleared": "Image cleared",
|
||||
"clickToUpload": "Click, drag, or paste to upload QR code image",
|
||||
"supportFormats": "Supports PNG, JPG, WEBP, Base64 formats",
|
||||
|
||||
"parseButton": "Parse QR Code",
|
||||
"parsing": "Parsing...",
|
||||
"resultLabel": "Parsing Result",
|
||||
"copyTooltip": "Copy",
|
||||
|
||||
"enterUrlError": "Please enter URL or text",
|
||||
"clickToChange": "Click to change image",
|
||||
|
||||
"pasteHint": "Supports Ctrl+V to paste images or Base64 strings",
|
||||
"autoGenerateHint": "QR code will be generated automatically after input",
|
||||
"autoParseHint": "QR code will be parsed automatically after upload"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"title": "Right Click Restorer",
|
||||
"description": "Detect and restore disabled browser right-click menus",
|
||||
"loading": "Loading...",
|
||||
"currentDomain": "Current Domain",
|
||||
"statusLocked": "Locked",
|
||||
"statusUnlocked": "Unlocked",
|
||||
"unsupported": "Unsupported",
|
||||
"unsupportedDesc": "The current page is a browser internal or extension page. Right-click unlock is not available. Please switch to a regular webpage.",
|
||||
"unlockDesc": "Click the button below to temporarily unlock the right-click menu for the current website. You will need to unlock again after refreshing the page.",
|
||||
"unlockBtn": "Unlock Right Click",
|
||||
"alreadyUnlocked": "Right Click Unlocked"
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"pageTitle": "Storage Cleaner",
|
||||
"pageSubtitle": "Clear cache, cookies, and local storage",
|
||||
"loading": "Loading...",
|
||||
"initializing": "Reading site data...",
|
||||
"occupied": "Occupied {{size}}",
|
||||
"cleaning": "Cleaning...",
|
||||
"cleanNow": "Clean Now",
|
||||
"autoRefresh": "Auto refresh page after cleaning",
|
||||
"selectAll": "Select all items",
|
||||
"cleanSuccess": "Cleaning complete",
|
||||
"cleanError": "Cleaning failed",
|
||||
"noData": "No data",
|
||||
"countUnit": "items",
|
||||
"errorNoTab": "Unable to get current tab",
|
||||
"errorRestricted": "Storage cleaning is not supported on this page",
|
||||
"cleanSuccessReload": "Cleaning successful, reloading page...",
|
||||
"errorStandardOnly": "Storage cleaning only works on standard web pages",
|
||||
"confirmTitle": "Confirm Clear Data?",
|
||||
"confirmDesc": "You are about to permanently delete the following selected storage items from the current page.",
|
||||
"irreversible": "This action is irreversible",
|
||||
"confirmAction": "Confirm Clear",
|
||||
"cleanedSummary": "Cleaned {{items}}",
|
||||
"noDataToClean": "No storage data found to clean",
|
||||
"partialFailure": "Some items failed to clean",
|
||||
"options": {
|
||||
"localStorage": "Local Storage",
|
||||
"sessionStorage": "Session Storage",
|
||||
"indexedDB": "IndexedDB",
|
||||
"cookies": "Cookies",
|
||||
"cacheStorage": "Cache Storage",
|
||||
"serviceWorkers": "Service Workers"
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"pageTitle": "Text Statistics",
|
||||
"pageSubtitle": "Real-time analysis of characters, words, lines, and byte size",
|
||||
"placeholder": "Type or paste text here...",
|
||||
"characters": "Characters",
|
||||
"words": "Words",
|
||||
"lines": "Lines",
|
||||
"bytes": "Byte Size"
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"pageTitle": "Timestamp Conversion",
|
||||
"pageSubtitle": "Unix millisecond conversion and formatting",
|
||||
"tsToDate": "Timestamp → Date",
|
||||
"dateToTs": "Date → Timestamp",
|
||||
"placeholderTs": "Enter timestamp...",
|
||||
"placeholderDate": "YYYY-MM-DD HH:mm:ss",
|
||||
"unitMs": "Millisecond (ms)",
|
||||
"unitS": "Second (s)",
|
||||
"convertButton": "Convert Now",
|
||||
"currentTs": "Current Timestamp",
|
||||
"useNowTooltip": "Use this value",
|
||||
"copyTsTooltip": "Copy timestamp",
|
||||
"usedSuccess": "Using current timestamp",
|
||||
"resultLabel": "Conversion Result",
|
||||
"copyResultTooltip": "Copy result",
|
||||
"relativeTime": "Relative Time",
|
||||
"iso8601": "ISO 8601",
|
||||
"utcTime": "UTC Time",
|
||||
"copyTooltip": "Copy",
|
||||
"resultEmpty": "Enter a value and click convert",
|
||||
"errors": {
|
||||
"invalidNumber": "Invalid number",
|
||||
"invalidTimestamp": "Invalid timestamp",
|
||||
"invalidFormat": "Format error"
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
{
|
||||
"pageTitle": "Base64 转换器",
|
||||
"pageSubtitle": "文本、文件与图像的 Base64 编码与解码",
|
||||
"textMode": "文本",
|
||||
"fileMode": "文件",
|
||||
"imageMode": "图像",
|
||||
"encode": "编码",
|
||||
"decode": "解码",
|
||||
"clear": "清空",
|
||||
"textInputPlaceholder": "输入需要编码为 Base64 的文本...",
|
||||
"base64InputPlaceholder": "输入需要解码的 Base64 字符串...",
|
||||
"base64Output": "Base64 编码结果",
|
||||
"textOutput": "解码文本结果",
|
||||
"copyRaw": "复制纯 Base64",
|
||||
"copyDataUri": "复制 Data URI",
|
||||
"clickOrDropToFile": "点击或拖拽文件到此处",
|
||||
"clickOrDropToImage": "点击或拖拽图像到此处",
|
||||
"clickOrDropToReplace": "点击或拖拽以替换文件",
|
||||
"maxFileSize": "最大文件大小:{{max}}",
|
||||
"supportedFormats": "支持 PNG、JPG、WEBP、GIF、BMP、SVG 等格式",
|
||||
"fileSizeExceeded": "文件大小超出限制(最大 {{max}})",
|
||||
"unsupportedImageType": "不支持的图像格式",
|
||||
"conversionFailed": "转换失败",
|
||||
"originalSize": "原始大小",
|
||||
"encodedSize": "编码大小",
|
||||
"invalidBase64": "Base64 字符串无效",
|
||||
"binaryDataDetected": "输入似乎是二进制数据(如图片)。请切换到「图像」选项卡。",
|
||||
"imageDataUriHint": "检测到图片的 data URI,请使用「图像」选项卡进行解码。",
|
||||
"switchToImageMode": "切换到图像模式",
|
||||
"download": "下载",
|
||||
"decodedFileName": "解码后文件名",
|
||||
"decodeBase64Placeholder": "输入需要解码的 Base64 或 data URI...",
|
||||
"decodedFileOutput": "解码文件",
|
||||
"decodedImageOutput": "解码图像",
|
||||
"inferredMimeType": "推断的 MIME 类型",
|
||||
"decodedSize": "解码大小"
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
{
|
||||
"appName": "测试工具",
|
||||
"buttons": {
|
||||
"save": "保存",
|
||||
"cancel": "取消",
|
||||
"confirm": "确认",
|
||||
"copy": "复制",
|
||||
"clear": "清理",
|
||||
"refresh": "刷新",
|
||||
"toggleLanguage": "切换语言",
|
||||
"toggleTheme": "切换主题",
|
||||
"themeMode": {
|
||||
"light": "切换到深色模式",
|
||||
"dark": "切换到系统模式",
|
||||
"system": "切换到浅色模式"
|
||||
},
|
||||
"search": "搜索工具...",
|
||||
"back": "返回",
|
||||
"clearSearch": "清除搜索",
|
||||
"recentSearch": "最近搜索",
|
||||
"noResults": "未找到相关工具",
|
||||
"openInTab": "在标签页打开",
|
||||
"settings": "设置"
|
||||
},
|
||||
"messages": {
|
||||
"copySuccess": "已复制到剪贴板",
|
||||
"copyError": "复制失败",
|
||||
"copyEmpty": "无内容可复制"
|
||||
},
|
||||
"errorBoundary": {
|
||||
"title": "糟糕,出了点问题",
|
||||
"description": "应用遇到了一些意外错误。您可以尝试刷新页面或重置应用。",
|
||||
"refresh": "刷新应用",
|
||||
"retry": "重新尝试"
|
||||
},
|
||||
"pageErrorBoundary": {
|
||||
"title": "该功能运行异常",
|
||||
"description": "该页面在加载或渲染时遇到了内部脚本错误。您可以尝试重试,或者通过导航菜单切换到其他工具。"
|
||||
},
|
||||
"router": {
|
||||
"notFound": "页面未找到",
|
||||
"notFoundDescription": "该功能在当前运行环境({{entryPointType}})下不可用或已被移除。"
|
||||
},
|
||||
"textInputArea": {
|
||||
"clear": "清空",
|
||||
"copyContent": "复制内容",
|
||||
"cleared": "已清空",
|
||||
"placeholder": "请输入文本"
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
{
|
||||
"dashboard": {
|
||||
"title": "仪表盘"
|
||||
},
|
||||
"timestamp": {
|
||||
"title": "时间戳",
|
||||
"description": "Unix 毫秒数转换与格式化"
|
||||
},
|
||||
"storageCleaner": {
|
||||
"title": "存储清理",
|
||||
"description": "清理缓存、Cookies 及本地存储"
|
||||
},
|
||||
"qrCode": {
|
||||
"title": "二维码工具",
|
||||
"description": "生成当前选中的 URL 的二维码"
|
||||
},
|
||||
"textStatistics": {
|
||||
"title": "文本统计",
|
||||
"description": "实时分析文本字符、单词及字节"
|
||||
},
|
||||
"jwt": {
|
||||
"title": "JWT 解析",
|
||||
"description": "JSON Web Token 解码与查看"
|
||||
},
|
||||
"jsonDiff": {
|
||||
"title": "JSON 工具",
|
||||
"description": "差异比较、格式化、YAML/TOML 转换及压缩"
|
||||
},
|
||||
"base64Converter": {
|
||||
"title": "Base64 转换器",
|
||||
"description": "文本、文件与图像的 Base64 编码转换"
|
||||
},
|
||||
"markdownToHtml": {
|
||||
"title": "Markdown 转 HTML",
|
||||
"description": "实时 Markdown 转换与 HTML 预览"
|
||||
},
|
||||
"htmlToMarkdown": {
|
||||
"title": "HTML 转 Markdown",
|
||||
"description": "实时 HTML 转换与 Markdown 预览"
|
||||
},
|
||||
"rightClickRestorer": {
|
||||
"title": "右键恢复",
|
||||
"description": "检测并恢复被网站禁用的浏览器右键菜单"
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"pageTitle": "HTML 转 Markdown",
|
||||
"pageSubtitle": "实时将 HTML 转换为 Markdown 格式",
|
||||
"splitMode": "分屏",
|
||||
"previewMode": "预览",
|
||||
"markdownMode": "Markdown",
|
||||
"clear": "清空",
|
||||
"inputLabel": "HTML 输入",
|
||||
"previewLabel": "Markdown 预览",
|
||||
"markdownOutputLabel": "Markdown 输出",
|
||||
"inputPlaceholder": "在此输入 HTML 内容...",
|
||||
"charCount": "{{count}} 字符",
|
||||
"copyMarkdown": "复制 Markdown",
|
||||
"download": "下载",
|
||||
"emptyHint": "输入 HTML 内容以查看转换结果"
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"pageTitle": "JSON 差异比较",
|
||||
"pageSubtitle": "对比两个 JSON 数据的差异",
|
||||
"leftPlaceholder": "输入原始 JSON...",
|
||||
"rightPlaceholder": "输入目标 JSON...",
|
||||
"leftLabel": "原始 JSON",
|
||||
"rightLabel": "目标 JSON",
|
||||
"compareButton": "比较",
|
||||
"clearButton": "清空",
|
||||
"sideBySideMode": "并排",
|
||||
"unifiedMode": "统一",
|
||||
"previousDiff": "上一个",
|
||||
"nextDiff": "下一个",
|
||||
"noDiffs": "无差异",
|
||||
"diffCount": "{{count}} 处差异",
|
||||
"invalidJson": "无效的 JSON 格式",
|
||||
"emptyHint": "输入两侧 JSON 后点击比较",
|
||||
"fixErrorHint": "请修正上方 JSON 的语法错误以开启实时流式比对",
|
||||
"added": "新增",
|
||||
"removed": "删除",
|
||||
"modified": "修改"
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
{
|
||||
"formatTitle": "JSON 格式化",
|
||||
"formatSubtitle": "美化和格式化 JSON 数据",
|
||||
"inputPlaceholder": "输入需要格式化的 JSON...",
|
||||
"formatButton": "格式化",
|
||||
"clearButton": "清空",
|
||||
"sortKeys": "键名排序",
|
||||
"sortKeysTooltip": "按字母顺序对 JSON 对象键进行排序",
|
||||
"indentSize": "缩进",
|
||||
"indentSpaces": "{{count}} 个空格",
|
||||
"outputLabel": "格式化结果",
|
||||
"copySuccess": "复制成功",
|
||||
"copyFail": "复制失败",
|
||||
"noContent": "无内容可复制",
|
||||
"invalidJson": "无效的 JSON 格式",
|
||||
"emptyHint": "输入 JSON 后点击格式化",
|
||||
"fixErrorHint": "请修正上方 JSON 的语法错误以开启实时流式格式化",
|
||||
"originalSize": "原始大小",
|
||||
"formattedSize": "格式化后大小",
|
||||
"diffMode": "差异比较",
|
||||
"formatMode": "格式化",
|
||||
"yamlMode": "YAML",
|
||||
"tomlMode": "TOML",
|
||||
"minifyMode": "压缩",
|
||||
"yamlTitle": "JSON 转 YAML",
|
||||
"yamlSubtitle": "将 JSON 数据转换为 YAML 格式",
|
||||
"yamlModeInputPlaceholder": "输入需要转换的 JSON...",
|
||||
"yamlModeOutputLabel": "YAML 结果",
|
||||
"yamlModeEmptyHint": "输入 JSON 后点击转换",
|
||||
"convertButton": "转换",
|
||||
"tomlTitle": "JSON 转 TOML",
|
||||
"tomlSubtitle": "将 JSON 数据转换为 TOML 格式",
|
||||
"tomlModeInputPlaceholder": "输入需要转换的 JSON...",
|
||||
"tomlModeOutputLabel": "TOML 结果",
|
||||
"tomlModeEmptyHint": "输入 JSON 后点击转换",
|
||||
"minifyTitle": "JSON 压缩",
|
||||
"minifySubtitle": "将 JSON 压缩为紧凑的单行格式",
|
||||
"minifyModeInputPlaceholder": "输入需要压缩的 JSON...",
|
||||
"minifyModeOutputLabel": "压缩结果",
|
||||
"minifyModeEmptyHint": "输入 JSON 后点击压缩",
|
||||
"minifyButton": "压缩"
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"pageTitle": "JWT 解析",
|
||||
"pageSubtitle": "JSON Web Token 解码与查看",
|
||||
"placeholder": "在此粘贴 JWT 令牌 (Encoded JWT)...",
|
||||
"headerTitle": "HEADER: 算法 & 令牌类型",
|
||||
"payloadTitle": "PAYLOAD: 数据",
|
||||
"signatureTitle": "签名",
|
||||
"noSignature": "无签名",
|
||||
"invalidFormat": "无法解析",
|
||||
"errors": {
|
||||
"invalidBase64String": "无效的 Base64URL 字符串",
|
||||
"failedToDecode": "Base64URL 解码失败:",
|
||||
"invalidFormat": "JWT 格式错误:必须包含三个由 . 分隔的部分",
|
||||
"parseHeaderFailed": "解析 Header 失败:",
|
||||
"parsePayloadFailed": "解析 Payload 失败:"
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"pageTitle": "Markdown 转 HTML",
|
||||
"pageSubtitle": "实时将 Markdown 转换为 HTML 并预览",
|
||||
"splitMode": "分屏",
|
||||
"previewMode": "预览",
|
||||
"htmlMode": "HTML",
|
||||
"clear": "清空",
|
||||
"print": "打印",
|
||||
"download": "下载",
|
||||
"inputLabel": "Markdown 输入",
|
||||
"previewLabel": "实时预览",
|
||||
"htmlOutputLabel": "HTML 输出",
|
||||
"inputPlaceholder": "在此输入 Markdown 内容...",
|
||||
"charCount": "{{count}} 字符",
|
||||
"copyHtml": "复制 HTML",
|
||||
"downloadSuccess": "文件下载成功",
|
||||
"printSuccess": "打印窗口已打开"
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
{
|
||||
"pageTitle": "二维码工具",
|
||||
"pageSubtitle": "生成和解析二维码",
|
||||
|
||||
"generateMode": "生成二维码",
|
||||
"parseMode": "解析二维码",
|
||||
|
||||
"urlToQr": "文本转二维码",
|
||||
"qrToUrl": "二维码转文本",
|
||||
|
||||
"urlInputLabel": "输入 URL 或文本",
|
||||
"urlInputPlaceholder": "请输入 URL 或文本内容,将自动生成二维码",
|
||||
"generateButton": "生成二维码",
|
||||
"generating": "生成中...",
|
||||
|
||||
"qrCodeWillShow": "二维码将显示在这里",
|
||||
"downloadButton": "下载二维码",
|
||||
"copyQrButton": "复制二维码",
|
||||
|
||||
"qrCodeSuccess": "二维码生成成功",
|
||||
"qrCodeDownloadSuccess": "二维码下载成功",
|
||||
"qrCodeCopySuccess": "二维码已复制到剪贴板",
|
||||
|
||||
"selectImage": "请选择二维码图片",
|
||||
"parseSuccess": "二维码解析成功",
|
||||
"noQrDetected": "未检测到二维码,请确保图片清晰且包含二维码",
|
||||
"parseError": "解析二维码失败,请重试",
|
||||
"generateError": "生成二维码失败,请检查输入内容",
|
||||
"copyError": "复制失败,请重试",
|
||||
|
||||
"imagePasted": "图片粘贴成功,正在解析...",
|
||||
"imagePasteError": "粘贴图片失败,请重试",
|
||||
"imageCleared": "图片已清除",
|
||||
"clickToUpload": "点击、拖拽或粘贴上传二维码图片",
|
||||
"supportFormats": "支持 PNG、JPG、WEBP、Base64 格式",
|
||||
|
||||
"parseButton": "解析二维码",
|
||||
"parsing": "解析中...",
|
||||
"resultLabel": "解析结果",
|
||||
"copyTooltip": "复制",
|
||||
|
||||
"enterUrlError": "请输入 URL 或文本",
|
||||
"clickToChange": "点击更换图片",
|
||||
|
||||
"pasteHint": "支持 Ctrl+V 粘贴图片或 Base64 字符串",
|
||||
"autoGenerateHint": "输入内容后将自动生成二维码",
|
||||
"autoParseHint": "上传图片后将自动解析二维码"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"title": "右键恢复",
|
||||
"description": "检测并恢复被网站禁用的浏览器右键菜单",
|
||||
"loading": "正在加载...",
|
||||
"currentDomain": "当前域名",
|
||||
"statusLocked": "未解锁",
|
||||
"statusUnlocked": "已解锁",
|
||||
"unsupported": "不支持",
|
||||
"unsupportedDesc": "当前页面为浏览器内部页面或扩展页面,无法解锁右键功能。请切换到普通网页后重试。",
|
||||
"unlockDesc": "点击下方按钮,为当前网站临时解锁右键菜单。刷新页面后需要重新解锁。",
|
||||
"unlockBtn": "解锁当前网站右键",
|
||||
"alreadyUnlocked": "右键已解锁"
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"pageTitle": "存储清理",
|
||||
"pageSubtitle": "清理缓存、Cookies 及本地存储",
|
||||
"loading": "加载中...",
|
||||
"initializing": "正在读取站点数据...",
|
||||
"occupied": "已占用 {{size}}",
|
||||
"cleaning": "正在清理...",
|
||||
"cleanNow": "立即清理",
|
||||
"autoRefresh": "清理后自动刷新页面",
|
||||
"selectAll": "全选所有项",
|
||||
"cleanSuccess": "清理完成",
|
||||
"cleanError": "清理失败",
|
||||
"noData": "无数据",
|
||||
"countUnit": "个",
|
||||
"errorNoTab": "无法获取当前标签页",
|
||||
"errorRestricted": "存储清理功能不支持此页面",
|
||||
"cleanSuccessReload": "清理成功,即将刷新页面",
|
||||
"errorStandardOnly": "存储清理功能仅适用于标准网页",
|
||||
"confirmTitle": "确认清理数据?",
|
||||
"confirmDesc": "您将永久删除当前页面的以下选定存储项。",
|
||||
"irreversible": "此操作不可撤销",
|
||||
"confirmAction": "确认清理",
|
||||
"cleanedSummary": "清理了 {{items}}",
|
||||
"noDataToClean": "该页面没有可清理的存储数据",
|
||||
"partialFailure": "部分清理失败",
|
||||
"options": {
|
||||
"localStorage": "Local Storage",
|
||||
"sessionStorage": "Session Storage",
|
||||
"indexedDB": "IndexedDB",
|
||||
"cookies": "Cookies",
|
||||
"cacheStorage": "Cache Storage",
|
||||
"serviceWorkers": "Service Workers"
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"pageTitle": "文本统计",
|
||||
"pageSubtitle": "实时分析文本的字符、单词、行数及字节大小",
|
||||
"placeholder": "在此输入或粘贴文本...",
|
||||
"characters": "字符数",
|
||||
"words": "单词数",
|
||||
"lines": "行数",
|
||||
"bytes": "字节大小"
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"pageTitle": "时间戳转换",
|
||||
"pageSubtitle": "Unix 毫秒数转换与格式化",
|
||||
"tsToDate": "时间戳 → 日期",
|
||||
"dateToTs": "日期 → 时间戳",
|
||||
"placeholderTs": "输入时间戳...",
|
||||
"placeholderDate": "YYYY-MM-DD HH:mm:ss",
|
||||
"unitMs": "毫秒 (ms)",
|
||||
"unitS": "秒 (s)",
|
||||
"convertButton": "立即转换",
|
||||
"currentTs": "当前时间戳",
|
||||
"useNowTooltip": "填充到下方",
|
||||
"copyTsTooltip": "复制时间戳",
|
||||
"usedSuccess": "已使用当前时间戳",
|
||||
"resultLabel": "转换结果",
|
||||
"copyResultTooltip": "复制结果",
|
||||
"relativeTime": "相对时间",
|
||||
"iso8601": "ISO 8601",
|
||||
"utcTime": "UTC 时间",
|
||||
"copyTooltip": "复制",
|
||||
"resultEmpty": "请输入并点击转换",
|
||||
"errors": {
|
||||
"invalidNumber": "无效数字",
|
||||
"invalidTimestamp": "无效时间戳",
|
||||
"invalidFormat": "格式错误"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user