refactor: 迁移 i18n 系统从 react-i18next 到 chrome.i18n

- 移除 react-i18next、i18next 及相关依赖
- 删除旧的 i18n/ 目录和 useLazyTranslation 工具
- 新增 utils/chromeI18n.ts 类型安全 wrapper(useI18n Hook + getMessage)
- 生成 public/_locales/{zh,en}/messages.json(298 个翻译 key)
- 批量更新 39+ 组件文件的导入和翻译调用
- 转换翻译键格式:namespace:key → namespace_key
- 修复 ErrorBoundary/PageErrorBoundary 从 withTranslation HOC 改为直接调用 getMessage
- 更新 vitest.setup.ts mock 加载实际翻译文本
- 修复 11 个测试文件的断言以匹配中文翻译
- TypeScript、ESLint、547 项测试全部通过

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
雨霖铃
2026-05-28 19:21:54 +08:00
parent 64e23994a6
commit aa23a263fe
94 changed files with 2987 additions and 1844 deletions
-61
View File
@@ -1,61 +0,0 @@
# i18n/
国际化资源目录,管理多语言翻译和 i18next 初始化配置。
## 目录结构
```
i18n/
├── index.ts # i18next 初始化配置
└── locales/
├── zh/ # 中文翻译(默认语言)
│ ├── common.json # 通用文案
│ ├── features.json # 功能模块标题和描述
│ ├── timestamp.json # 时间戳工具翻译
│ ├── storageCleaner.json # 存储清理工具翻译
│ ├── qrCode.json # 二维码工具翻译
│ ├── textStatistics.json # 文本统计工具翻译
│ ├── jwt.json # JWT 工具翻译
│ ├── jsonDiff.json # JSON 差异工具翻译
│ ├── jsonFormat.json # JSON 格式化工具翻译
│ ├── base64Converter.json
│ ├── markdownToHtml.json
│ ├── htmlToMarkdown.json
│ └── rightClickRestorer.json
└── en/ # 英文翻译(结构同上)
└── ...
```
## index.ts
i18next 初始化配置:
- 同步加载 `common``features` 核心命名空间
- 自定义 `chromeStorage` 语言检测器,从 Chrome Storage 读取语言偏好
- `normalizeLanguage()` 将任意语言标识归一化为 `zh``en`
- 语言变更时同步更新 Day.js 本地化和 localStorage 快照
## 翻译键格式
- 命名空间:`common`(默认)、`features`、各功能独立命名空间
- 键格式:`namespace:key`(如 `features:timestamp.title``timestamp:unitMs`
## 使用方式
```tsx
// 页面组件 — 懒加载翻译
import { useLazyTranslation } from '@/utils/useLazyTranslation';
const { t } = useLazyTranslation('timestamp');
t('timestamp:title');
// 全局组件 — 直接使用
import { useTranslation } from 'react-i18next';
const { t } = useTranslation(['common', 'features']);
t('common:settings');
```
## 添加新翻译
1.`locales/{zh,en}/features.json` 添加功能标题和描述
2. 创建 `locales/{zh,en}/{功能名}.json` 添加功能专属翻译
3.`utils/useLazyTranslation.ts``localeModules` 中注册新命名空间
-144
View File
@@ -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;
-37
View File
@@ -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"
}
-50
View File
@@ -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"
}
}
-45
View File
@@ -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"
}
}
-16
View File
@@ -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"
}
-22
View File
@@ -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"
}
-42
View File
@@ -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"
}
-17
View File
@@ -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: "
}
}
-18
View File
@@ -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"
}
-48
View File
@@ -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"
}
-13
View File
@@ -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"
}
-34
View File
@@ -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"
}
}
-9
View File
@@ -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"
}
-27
View File
@@ -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"
}
}
-37
View File
@@ -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": "解码大小"
}
-50
View File
@@ -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": "请输入文本"
}
}
-45
View File
@@ -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": "检测并恢复被网站禁用的浏览器右键菜单"
}
}
-16
View File
@@ -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 内容以查看转换结果"
}
-22
View File
@@ -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": "修改"
}
-42
View File
@@ -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": "压缩"
}
-17
View File
@@ -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 失败:"
}
}
-18
View File
@@ -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": "打印窗口已打开"
}
-48
View File
@@ -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": "上传图片后将自动解析二维码"
}
-13
View File
@@ -1,13 +0,0 @@
{
"title": "右键恢复",
"description": "检测并恢复被网站禁用的浏览器右键菜单",
"loading": "正在加载...",
"currentDomain": "当前域名",
"statusLocked": "未解锁",
"statusUnlocked": "已解锁",
"unsupported": "不支持",
"unsupportedDesc": "当前页面为浏览器内部页面或扩展页面,无法解锁右键功能。请切换到普通网页后重试。",
"unlockDesc": "点击下方按钮,为当前网站临时解锁右键菜单。刷新页面后需要重新解锁。",
"unlockBtn": "解锁当前网站右键",
"alreadyUnlocked": "右键已解锁"
}
-34
View File
@@ -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"
}
}
-9
View File
@@ -1,9 +0,0 @@
{
"pageTitle": "文本统计",
"pageSubtitle": "实时分析文本的字符、单词、行数及字节大小",
"placeholder": "在此输入或粘贴文本...",
"characters": "字符数",
"words": "单词数",
"lines": "行数",
"bytes": "字节大小"
}
-27
View File
@@ -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": "格式错误"
}
}