refactor(i18n): 移除 chrome.i18n 国际化,统一使用中文硬编码
移除 chromeI18n 工具、_locales 翻译文件和 manifest default_locale 配置, 将所有 UI 文案改为直接硬编码中文,并更新相关测试与文档。
This commit is contained in:
@@ -1,68 +0,0 @@
|
||||
/**
|
||||
* chrome.i18n 类型安全 wrapper
|
||||
* 提供与 react-i18next 兼容的接口
|
||||
*/
|
||||
|
||||
/**
|
||||
* 获取翻译文本
|
||||
* @param msgId 翻译 key(如 'timestamp_pageTitle')
|
||||
* @param substitutions 占位符替换值(可选)
|
||||
* @returns 翻译后的文本
|
||||
*/
|
||||
export function getMessage(msgId: string, substitutions?: string[]): string {
|
||||
try {
|
||||
return chrome.i18n.getMessage(msgId, substitutions);
|
||||
} catch (error) {
|
||||
console.warn(`[chrome.i18n] 无法获取翻译: ${msgId}`, error);
|
||||
return msgId; // 回退到 key 本身
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* react-i18next 兼容的 Hook
|
||||
* 返回 t 函数和相关信息
|
||||
*/
|
||||
export function useI18n(namespace?: string | string[]) {
|
||||
const namespaces = Array.isArray(namespace) ? namespace : namespace ? [namespace] : [];
|
||||
|
||||
const t = (key: string, options?: Record<string, unknown>): string => {
|
||||
// 统一将分隔符转换为下划线,兼容 'namespace:key.path' 和 'key.path' 两种写法
|
||||
const msgId = key.replace(':', '_').replace(/\./g, '_');
|
||||
|
||||
// 先尝试直接查找 key
|
||||
let message = getMessage(msgId);
|
||||
|
||||
// 如果直接查找未命中(空字符串或返回 key 本身),尝试命名空间前缀(使用转换后的 msgId)
|
||||
if ((!message || message === msgId) && namespaces.length > 0) {
|
||||
for (const ns of namespaces) {
|
||||
const candidate = `${ns}_${msgId}`;
|
||||
const result = getMessage(candidate);
|
||||
if (result !== candidate) {
|
||||
message = result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (options) {
|
||||
for (const [placeholder, value] of Object.entries(options)) {
|
||||
message = message.replace(`{{${placeholder}}}`, String(value));
|
||||
}
|
||||
}
|
||||
|
||||
return message;
|
||||
};
|
||||
|
||||
return {
|
||||
t,
|
||||
i18n: {
|
||||
language: 'zh',
|
||||
changeLanguage: (_lng?: string) => {
|
||||
// chrome.i18n 无法动态切换语言,需要刷新页面
|
||||
console.warn('[chrome.i18n] 无法动态切换语言,需要刷新页面');
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
isLoaded: true,
|
||||
};
|
||||
}
|
||||
+7
-12
@@ -2,8 +2,6 @@
|
||||
* JWT 解析工具
|
||||
*/
|
||||
|
||||
import { getMessage } from '@/utils/chromeI18n';
|
||||
|
||||
interface JwtHeader {
|
||||
alg: string;
|
||||
typ?: string;
|
||||
@@ -43,7 +41,7 @@ export function decodeBase64Url(str: string): string {
|
||||
const pad = base64.length % 4;
|
||||
if (pad) {
|
||||
if (pad === 1) {
|
||||
throw new Error(getMessage('jwt_errors_invalidBase64String'));
|
||||
throw new Error('无效的 Base64URL 字符串');
|
||||
}
|
||||
base64 += new Array(5 - pad).join('=');
|
||||
}
|
||||
@@ -58,10 +56,9 @@ export function decodeBase64Url(str: string): string {
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
return decoder.decode(bytes);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
getMessage('jwt_errors_failedToDecode') + (e instanceof Error ? e.message : String(e)),
|
||||
{ cause: e },
|
||||
);
|
||||
throw new Error('Base64 解码失败: ' + (e instanceof Error ? e.message : String(e)), {
|
||||
cause: e,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +75,7 @@ export function parseJwt(token: string): JwtResult {
|
||||
payload: null,
|
||||
signature: '',
|
||||
raw: { header: '', payload: '', signature: '' },
|
||||
error: getMessage('jwt_errors_invalidFormat'),
|
||||
error: 'JWT 格式无效:应包含 3 个部分(header.payload.signature)',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -98,8 +95,7 @@ export function parseJwt(token: string): JwtResult {
|
||||
const headerJson = decodeBase64Url(headerB64);
|
||||
result.header = JSON.parse(headerJson);
|
||||
} catch (e) {
|
||||
result.error =
|
||||
getMessage('jwt_errors_parseHeaderFailed') + (e instanceof Error ? e.message : String(e));
|
||||
result.error = 'JWT Header 解析失败: ' + (e instanceof Error ? e.message : String(e));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -107,8 +103,7 @@ export function parseJwt(token: string): JwtResult {
|
||||
const payloadJson = decodeBase64Url(payloadB64);
|
||||
result.payload = JSON.parse(payloadJson);
|
||||
} catch (e) {
|
||||
result.error =
|
||||
getMessage('jwt_errors_parsePayloadFailed') + (e instanceof Error ? e.message : String(e));
|
||||
result.error = 'JWT Payload 解析失败: ' + (e instanceof Error ? e.message : String(e));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { DataRule, FieldConfig } from '@/types/testDataGenerator';
|
||||
const STORAGE_KEY = 'testDataGenerator_rules';
|
||||
|
||||
/** 最大规则数量 */
|
||||
const MAX_RULES = 20;
|
||||
export const MAX_RULES = 20;
|
||||
|
||||
/**
|
||||
* 获取所有规则
|
||||
|
||||
@@ -20,6 +20,15 @@ const CLEAN_OPTION_KEYS: (keyof StorageCleanerOptions)[] = [
|
||||
'serviceWorkers',
|
||||
];
|
||||
|
||||
const OPTION_LABELS: Record<string, string> = {
|
||||
localStorage: 'Local Storage',
|
||||
sessionStorage: 'Session Storage',
|
||||
indexedDB: '站点存储',
|
||||
cookies: 'Cookies',
|
||||
cacheStorage: 'Cache Storage',
|
||||
serviceWorkers: 'Service Workers',
|
||||
};
|
||||
|
||||
export async function getCurrentTab() {
|
||||
// For popup pages, we need to get the active tab from the browser window that triggered the popup.
|
||||
// We should ONLY care about the currently active tab in the last focused window.
|
||||
@@ -362,22 +371,19 @@ export async function clearStorage(
|
||||
return result;
|
||||
}
|
||||
|
||||
export function formatCleaningResult(
|
||||
result: CleaningResult,
|
||||
t: (key: string, options?: Record<string, unknown>) => string,
|
||||
): string {
|
||||
export function formatCleaningResult(result: CleaningResult): string {
|
||||
const parts: string[] = [];
|
||||
|
||||
for (const key of CLEAN_OPTION_KEYS) {
|
||||
const r = result[key];
|
||||
if (r?.success && r.count > 0) {
|
||||
parts.push(`${r.count} ${t(`storageCleaner:options.${key}`)}`);
|
||||
parts.push(`${r.count} ${OPTION_LABELS[key] || key}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (parts.length === 0) {
|
||||
return t('storageCleaner:noDataToClean');
|
||||
return '该页面没有可清理的存储数据';
|
||||
}
|
||||
|
||||
return t('storageCleaner:cleanedSummary', { items: parts.join(', ') });
|
||||
return `清理了 ${parts.join(', ')}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user