feat(i18n): replace hardcoded Chinese messages with English
This commit is contained in:
1 parent
89bdbc7aea
commit
9c67b9d40c
77 files changed
+2385
-2304
No files matched your search
+3
-3
@@ -193,7 +193,7 @@ function AppContent() {
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('读取关闭行为设置失败', error);
|
||||
console.error('Failed to read close behavior settings', error);
|
||||
}
|
||||
|
||||
setWindowsClosePrompt((current) =>
|
||||
@@ -216,7 +216,7 @@ function AppContent() {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('监听 Windows 关闭确认事件失败', error);
|
||||
console.error('Failed to listen for Windows close confirmation events', error);
|
||||
});
|
||||
|
||||
return () => {
|
||||
@@ -252,7 +252,7 @@ function AppContent() {
|
||||
try {
|
||||
await invoke('open_external_url', { url: CONTACT_URL });
|
||||
} catch (error) {
|
||||
console.error('打开联系我们链接失败', error);
|
||||
console.error('Failed to open the contact link', error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ export class AppErrorBoundary extends Component<Props, State> {
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, info: ErrorInfo) {
|
||||
console.error('EasyCLIProxyAPI 渲染异常', error, info.componentStack);
|
||||
console.error('EasyCLIProxyAPI render failure', error, info.componentStack);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
+1
-1
@@ -99,7 +99,7 @@ export function CoreRuntimeProvider({ children }: { children: ReactNode }) {
|
||||
export function useCoreRuntime() {
|
||||
const context = useContext(CoreRuntimeContext);
|
||||
if (!context) {
|
||||
throw new Error('useCoreRuntime 必须在 CoreRuntimeProvider 内使用');
|
||||
throw new Error('useCoreRuntime must be used inside CoreRuntimeProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
+1
-1
@@ -194,7 +194,7 @@ export function CoreUpdateProvider({ children }: { children: ReactNode }) {
|
||||
export function useCoreUpdate() {
|
||||
const context = useContext(CoreUpdateContext);
|
||||
if (!context) {
|
||||
throw new Error('useCoreUpdate 必须在 CoreUpdateProvider 内使用');
|
||||
throw new Error('useCoreUpdate must be used inside CoreUpdateProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
+5
-5
@@ -16,7 +16,7 @@ export const languageOptions: ReadonlyArray<{
|
||||
|
||||
const STORAGE_KEY = 'easy-cli-proxy-api.locale';
|
||||
const resources = { 'zh-CN': zhCN, 'zh-TW': zhTW, ja, en } as const;
|
||||
let currentLocale: AppLocale = 'zh-CN';
|
||||
let currentLocale: AppLocale = 'en';
|
||||
|
||||
export const supportedLocales: readonly AppLocale[] = ['zh-CN', 'zh-TW', 'ja', 'en'];
|
||||
|
||||
@@ -26,7 +26,6 @@ export function getCurrentLocale(): AppLocale {
|
||||
|
||||
export function normalizeLocale(value: string | null | undefined): AppLocale {
|
||||
const normalized = value?.trim().toLowerCase() ?? '';
|
||||
if (normalized.startsWith('en')) return 'en';
|
||||
if (normalized.startsWith('ja')) return 'ja';
|
||||
if (
|
||||
normalized === 'zh-tw'
|
||||
@@ -34,11 +33,12 @@ export function normalizeLocale(value: string | null | undefined): AppLocale {
|
||||
|| normalized === 'zh-mo'
|
||||
|| normalized.startsWith('zh-hant')
|
||||
) return 'zh-TW';
|
||||
return 'zh-CN';
|
||||
if (normalized.startsWith('zh')) return 'zh-CN';
|
||||
return 'en';
|
||||
}
|
||||
|
||||
function detectInitialLocale(): AppLocale {
|
||||
if (typeof window === 'undefined') return 'zh-CN';
|
||||
if (typeof window === 'undefined') return 'en';
|
||||
try {
|
||||
const saved = window.localStorage.getItem(STORAGE_KEY);
|
||||
if (saved) return normalizeLocale(saved);
|
||||
@@ -55,7 +55,7 @@ function interpolate(template: string, variables?: MessageVariables): string {
|
||||
}
|
||||
|
||||
export function translate(locale: AppLocale, key: MessageKey, variables?: MessageVariables): string {
|
||||
return interpolate(resources[locale][key] ?? zhCN[key], variables);
|
||||
return interpolate(resources[locale][key] ?? en[key], variables);
|
||||
}
|
||||
|
||||
type I18nContextValue = {
|
||||
|
||||
@@ -1461,6 +1461,7 @@ export const jaOverrides = {
|
||||
'kernel.error.port': 'ポートは 1~65535 の範囲で指定してください',
|
||||
'kernel.error.installTask': 'インストールタスクを読み込めませんでした:{error}',
|
||||
'kernel.install.installingVersion': '{version} をインストールしています',
|
||||
'kernel.install.installingLatest': '最新バージョンをインストールしています',
|
||||
'kernel.phase.preparingDownload': 'ダウンロードを準備中',
|
||||
'kernel.phase.downloading': 'ダウンロード中',
|
||||
'kernel.phase.extracting': '展開中',
|
||||
|
||||
@@ -1461,6 +1461,7 @@ export const en: Record<MessageKey, string> = {
|
||||
'kernel.error.port': 'The port must be between 1 and 65535',
|
||||
'kernel.error.installTask': 'Failed to read the installation task: {error}',
|
||||
'kernel.install.installingVersion': 'Installing {version}',
|
||||
'kernel.install.installingLatest': 'Installing the latest version',
|
||||
'kernel.phase.preparingDownload': 'Preparing Download',
|
||||
'kernel.phase.downloading': 'Downloading',
|
||||
'kernel.phase.extracting': 'Extracting',
|
||||
|
||||
@@ -1460,6 +1460,7 @@ export const zhCN = {
|
||||
'kernel.error.port': '端口必须在 1 到 65535 之间',
|
||||
'kernel.error.installTask': '读取安装任务失败: {error}',
|
||||
'kernel.install.installingVersion': '正在安装 {version}',
|
||||
'kernel.install.installingLatest': '正在安装最新版本',
|
||||
'kernel.phase.preparingDownload': '准备下载',
|
||||
'kernel.phase.downloading': '下载中',
|
||||
'kernel.phase.extracting': '解压中',
|
||||
|
||||
+10
-10
@@ -35,18 +35,18 @@ function installToolbar(scenario: BrowserMockScenario, delayMs: number) {
|
||||
const toolbar = document.createElement('aside');
|
||||
toolbar.id = 'browser-mock-toolbar';
|
||||
toolbar.setAttribute('aria-label', 'Browser Mock controls');
|
||||
toolbar.title = '仅在普通浏览器开发模式中显示;Tauri 和正式构建不会启用 Mock。';
|
||||
toolbar.title = 'Shown only in regular browser development mode; Mock is disabled in Tauri and production builds.';
|
||||
|
||||
const badge = document.createElement('strong');
|
||||
badge.textContent = 'MOCK';
|
||||
|
||||
const select = document.createElement('select');
|
||||
select.setAttribute('aria-label', 'Browser Mock 场景');
|
||||
select.setAttribute('aria-label', 'Browser Mock scenario');
|
||||
const scenarios: Array<[BrowserMockScenario, string]> = [
|
||||
['running', '内核运行'],
|
||||
['stopped', '内核停止'],
|
||||
['empty', '未安装内核'],
|
||||
['error', '请求错误'],
|
||||
['running', 'Core running'],
|
||||
['stopped', 'Core stopped'],
|
||||
['empty', 'Core not installed'],
|
||||
['error', 'Request error'],
|
||||
];
|
||||
scenarios.forEach(([value, label]) => {
|
||||
const option = document.createElement('option');
|
||||
@@ -64,7 +64,7 @@ function installToolbar(scenario: BrowserMockScenario, delayMs: number) {
|
||||
});
|
||||
|
||||
const hint = document.createElement('span');
|
||||
hint.textContent = delayMs > 0 ? `${delayMs} ms` : '浏览器调试';
|
||||
hint.textContent = delayMs > 0 ? `${delayMs} ms` : 'Browser debugging';
|
||||
|
||||
toolbar.append(badge, select, hint);
|
||||
document.body.append(toolbar);
|
||||
@@ -74,7 +74,7 @@ function installToolbar(scenario: BrowserMockScenario, delayMs: number) {
|
||||
export function installBrowserMock(): InstalledBrowserMock | null {
|
||||
const options = resolveBrowserMockOptions(window.location.search, readStoredScenario());
|
||||
if (options.mode === 'off') {
|
||||
console.info('[Browser Mock] 已通过 ?mock=off 禁用。');
|
||||
console.info('[Browser Mock] Disabled via ?mock=off.');
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -98,8 +98,8 @@ export function installBrowserMock(): InstalledBrowserMock | null {
|
||||
installToolbar(options.mode, options.delayMs);
|
||||
|
||||
console.info(
|
||||
`[Browser Mock] 已启用场景“${options.mode}”${options.delayMs ? `,延迟 ${options.delayMs} ms` : ''}。`,
|
||||
'使用 ?mock=running|stopped|empty|error 切换,?mock=off 禁用。',
|
||||
`[Browser Mock] Enabled scenario “${options.mode}”${options.delayMs ? ` with a ${options.delayMs} ms delay` : ''}.`,
|
||||
'Use ?mock=running|stopped|empty|error to switch scenarios, or ?mock=off to disable.',
|
||||
);
|
||||
return { scenario: options.mode, delayMs: options.delayMs };
|
||||
}
|
||||
@@ -83,7 +83,7 @@ function createCoreStatus(scenario: BrowserMockScenario) {
|
||||
currentVersion: installed ? '7.3.15' : null,
|
||||
installDir: 'C:\\EasyCLIProxyAPI\\cpa-core',
|
||||
binaryPath: installed ? 'C:\\EasyCLIProxyAPI\\cpa-core\\cli-proxy-api.exe' : null,
|
||||
message: running ? 'Browser Mock 内核运行中' : installed ? 'Browser Mock 内核已停止' : 'Browser Mock 内核未安装',
|
||||
message: running ? 'Browser Mock core is running' : installed ? 'Browser Mock core is stopped' : 'Browser Mock core is not installed',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ function createUsageEvents() {
|
||||
endpoint: '/v1/responses',
|
||||
api_key_hash: 'mock-key-hash',
|
||||
api_key_display: 'sk-mock••••••••2026',
|
||||
api_key_remark: '浏览器 Mock 密钥',
|
||||
api_key_remark: 'Browser Mock key',
|
||||
tokens: {
|
||||
input_tokens: input,
|
||||
output_tokens: output,
|
||||
@@ -186,10 +186,10 @@ function createAgentStatuses() {
|
||||
pluginInstalled: id === 'pi',
|
||||
launchTargets: id === 'codex' || id === 'opencode'
|
||||
? [
|
||||
{ id: 'app', label: 'Desktop App', detail: 'Browser Mock 桌面入口' },
|
||||
{ id: 'cli', label: 'CLI', detail: 'Browser Mock 命令行入口' },
|
||||
{ id: 'app', label: 'Desktop App', detail: 'Browser Mock desktop entry point' },
|
||||
{ id: 'cli', label: 'CLI', detail: 'Browser Mock command-line entry point' },
|
||||
]
|
||||
: [{ id: id === 'claude-desktop' ? 'app' : 'cli', label: '启动', detail: 'Browser Mock 启动目标' }],
|
||||
: [{ id: id === 'claude-desktop' ? 'app' : 'cli', label: 'Launch', detail: 'Browser Mock launch target' }],
|
||||
version: `1.${index + 4}.0`,
|
||||
cliVersion: id === 'claude-desktop' ? null : `1.${index + 4}.0`,
|
||||
appVersion: id === 'claude-desktop' ? '0.12.8' : null,
|
||||
@@ -217,7 +217,7 @@ function createAgentStatuses() {
|
||||
],
|
||||
}
|
||||
: null,
|
||||
warnings: id === 'openclaw' ? ['Mock:尚未应用配置'] : [],
|
||||
warnings: id === 'openclaw' ? ['Mock: configuration has not been applied'] : [],
|
||||
error: null,
|
||||
}));
|
||||
}
|
||||
@@ -345,7 +345,7 @@ function createState(scenario: BrowserMockScenario) {
|
||||
{ key: 'other', label: 'Other', requests: 34, failures: 1, tokens: 333_300 },
|
||||
],
|
||||
apiKeys: [
|
||||
{ key: 'mock-key-hash', label: '浏览器 Mock 密钥', requests: 312, failures: 7, tokens: 2_187_300 },
|
||||
{ key: 'mock-key-hash', label: 'Browser Mock key', requests: 312, failures: 7, tokens: 2_187_300 },
|
||||
],
|
||||
};
|
||||
return {
|
||||
@@ -359,8 +359,8 @@ function createState(scenario: BrowserMockScenario) {
|
||||
},
|
||||
coreConfig: {
|
||||
apiKeys: [
|
||||
{ apiKey: 'sk-browser-mock-2026', remark: '浏览器 Mock 密钥' },
|
||||
{ apiKey: 'sk-browser-demo-secondary', remark: '备用演示密钥' },
|
||||
{ apiKey: 'sk-browser-mock-2026', remark: 'Browser Mock key' },
|
||||
{ apiKey: 'sk-browser-demo-secondary', remark: 'Secondary demo key' },
|
||||
],
|
||||
debug: false,
|
||||
commercialMode: false,
|
||||
@@ -390,7 +390,7 @@ function createState(scenario: BrowserMockScenario) {
|
||||
silentStartEnabled: false,
|
||||
defaultTerminal: 'auto',
|
||||
availableTerminals: [
|
||||
{ id: 'auto', label: '自动选择' },
|
||||
{ id: 'auto', label: 'Automatic selection' },
|
||||
{ id: 'windows-terminal', label: 'Windows Terminal' },
|
||||
{ id: 'powershell', label: 'PowerShell' },
|
||||
],
|
||||
@@ -512,7 +512,7 @@ function createState(scenario: BrowserMockScenario) {
|
||||
models: ['gpt-5.2-codex', 'gpt-5.1-codex'].map((slug) => {
|
||||
const configuration = {
|
||||
display_name: slug === 'gpt-5.2-codex' ? 'GPT-5.2 Codex' : 'GPT-5.1 Codex',
|
||||
description: 'Browser Mock 模型目录',
|
||||
description: 'Browser Mock model catalog',
|
||||
context_window: 272_000,
|
||||
max_context_window: 400_000,
|
||||
effective_context_window_percent: 90,
|
||||
@@ -545,9 +545,9 @@ function createState(scenario: BrowserMockScenario) {
|
||||
configured: true,
|
||||
},
|
||||
codexSessions: [
|
||||
{ id: 'mock-session-1', title: '实现 Browser Mock', cwd: 'E:\\projects\\mock-demo', modelProvider: 'cpa-gui', archived: false, updatedAtMs: Date.now() - 20 * 60_000, databasePath: 'C:\\Users\\Mock\\.codex\\state.sqlite' },
|
||||
{ id: 'mock-session-2', title: 'TypeScript 类型学习', cwd: 'E:\\projects\\typescript-study', modelProvider: 'openai', archived: false, updatedAtMs: Date.now() - 3 * 60 * 60_000, databasePath: 'C:\\Users\\Mock\\.codex\\state.sqlite' },
|
||||
{ id: 'mock-session-3', title: '旧版界面检查', cwd: 'E:\\projects\\legacy-ui', modelProvider: 'cpa-gui', archived: true, updatedAtMs: Date.now() - 3 * 24 * 60 * 60_000, databasePath: 'C:\\Users\\Mock\\.codex\\state.sqlite' },
|
||||
{ id: 'mock-session-1', title: 'Implement Browser Mock', cwd: 'E:\\projects\\mock-demo', modelProvider: 'cpa-gui', archived: false, updatedAtMs: Date.now() - 20 * 60_000, databasePath: 'C:\\Users\\Mock\\.codex\\state.sqlite' },
|
||||
{ id: 'mock-session-2', title: 'Learn TypeScript types', cwd: 'E:\\projects\\typescript-study', modelProvider: 'openai', archived: false, updatedAtMs: Date.now() - 3 * 60 * 60_000, databasePath: 'C:\\Users\\Mock\\.codex\\state.sqlite' },
|
||||
{ id: 'mock-session-3', title: 'Review legacy UI', cwd: 'E:\\projects\\legacy-ui', modelProvider: 'cpa-gui', archived: true, updatedAtMs: Date.now() - 3 * 24 * 60 * 60_000, databasePath: 'C:\\Users\\Mock\\.codex\\state.sqlite' },
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -769,21 +769,21 @@ export function createBrowserMockRuntime(
|
||||
|
||||
case 'get_core_status': return clone(state.coreStatus);
|
||||
case 'start_core_process': {
|
||||
if (!state.coreStatus.installed) throw new Error('Browser Mock:请先安装内核');
|
||||
Object.assign(state.coreStatus, { running: true, ready: true, managed: true, starting: false, processId: 42817, message: 'Browser Mock 内核运行中' });
|
||||
if (!state.coreStatus.installed) throw new Error('Browser Mock: install the core first');
|
||||
Object.assign(state.coreStatus, { running: true, ready: true, managed: true, starting: false, processId: 42817, message: 'Browser Mock core is running' });
|
||||
state.guiSettings.runOnStartup = true;
|
||||
emit('core-status-changed', state.coreStatus);
|
||||
return clone(state.coreStatus);
|
||||
}
|
||||
case 'stop_core_process': {
|
||||
Object.assign(state.coreStatus, { running: false, ready: false, managed: false, starting: false, processId: null, message: 'Browser Mock 内核已停止' });
|
||||
Object.assign(state.coreStatus, { running: false, ready: false, managed: false, starting: false, processId: null, message: 'Browser Mock core is stopped' });
|
||||
state.guiSettings.runOnStartup = false;
|
||||
emit('core-status-changed', state.coreStatus);
|
||||
return clone(state.coreStatus);
|
||||
}
|
||||
case 'restart_core_process': {
|
||||
if (!state.coreStatus.installed) throw new Error('Browser Mock:请先安装内核');
|
||||
Object.assign(state.coreStatus, { running: true, ready: true, managed: true, starting: false, processId: 42818, message: 'Browser Mock 内核已重启' });
|
||||
if (!state.coreStatus.installed) throw new Error('Browser Mock: install the core first');
|
||||
Object.assign(state.coreStatus, { running: true, ready: true, managed: true, starting: false, processId: 42818, message: 'Browser Mock core restarted' });
|
||||
emit('core-status-changed', state.coreStatus);
|
||||
return clone(state.coreStatus);
|
||||
}
|
||||
@@ -853,13 +853,13 @@ export function createBrowserMockRuntime(
|
||||
downloadedBytes: 18 * 1024 * 1024,
|
||||
totalBytes: state.appUpdateInfo.downloadSizeBytes,
|
||||
percent: 38,
|
||||
message: 'Browser Mock 正在下载更新',
|
||||
message: 'Browser Mock is downloading an update',
|
||||
});
|
||||
emit('app-update-progress', state.appUpdateTask);
|
||||
return null;
|
||||
}
|
||||
case 'cancel_app_update': {
|
||||
Object.assign(state.appUpdateTask, { running: false, cancellable: false, phase: 'cancelled', message: 'Browser Mock 已取消更新' });
|
||||
Object.assign(state.appUpdateTask, { running: false, cancellable: false, phase: 'cancelled', message: 'Browser Mock update canceled' });
|
||||
emit('app-update-progress', state.appUpdateTask);
|
||||
return null;
|
||||
}
|
||||
@@ -873,8 +873,8 @@ export function createBrowserMockRuntime(
|
||||
installDir: state.coreStatus.installDir,
|
||||
binaryPath: 'C:\\EasyCLIProxyAPI\\cpa-core\\cli-proxy-api.exe',
|
||||
};
|
||||
Object.assign(state.coreStatus, { installed: true, currentVersion: result.version, binaryPath: result.binaryPath, message: 'Browser Mock 内核已安装' });
|
||||
Object.assign(state.coreInstallTask, { running: false, cancellable: false, phase: 'completed', downloaded: 64 * 1024 * 1024, total: 64 * 1024 * 1024, percent: 100, message: 'Browser Mock 安装完成', result });
|
||||
Object.assign(state.coreStatus, { installed: true, currentVersion: result.version, binaryPath: result.binaryPath, message: 'Browser Mock core installed' });
|
||||
Object.assign(state.coreInstallTask, { running: false, cancellable: false, phase: 'completed', downloaded: 64 * 1024 * 1024, total: 64 * 1024 * 1024, percent: 100, message: 'Browser Mock installation complete', result });
|
||||
emit('core-install-progress', state.coreInstallTask);
|
||||
emit('core-status-changed', state.coreStatus);
|
||||
return clone(result);
|
||||
@@ -915,7 +915,7 @@ export function createBrowserMockRuntime(
|
||||
case 'provider_health_probe': return { firstTokenLatencyMs: 184, responseLatencyMs: 642 };
|
||||
|
||||
case 'list_oauth_browsers': return [
|
||||
{ id: 'default', label: '系统默认浏览器' },
|
||||
{ id: 'default', label: 'System default browser' },
|
||||
{ id: 'chrome', label: 'Google Chrome (Mock)' },
|
||||
{ id: 'edge', label: 'Microsoft Edge (Mock)' },
|
||||
];
|
||||
@@ -940,7 +940,7 @@ export function createBrowserMockRuntime(
|
||||
|
||||
case 'get_usage_collector_status': return {
|
||||
state: state.coreStatus.ready ? 'collecting' : 'waiting-core',
|
||||
message: state.coreStatus.ready ? 'Browser Mock 正在采集使用记录' : '等待内核启动',
|
||||
message: state.coreStatus.ready ? 'Browser Mock is collecting usage records' : 'Waiting for the core to start',
|
||||
lastCollectedAt: new Date().toISOString(),
|
||||
totalRecords: state.usageOverview.totalRequests,
|
||||
};
|
||||
@@ -1152,7 +1152,7 @@ export function createBrowserMockRuntime(
|
||||
const ids = asArray(request.sessionIds).map(String);
|
||||
state.codexSessions = state.codexSessions.filter((session) => !ids.includes(session.id));
|
||||
return {
|
||||
results: ids.map((id) => ({ sessionId: id, status: 'deleted', message: 'Browser Mock 已删除', backupPath: 'C:\\Users\\Mock\\.codex\\backups\\mock.zip' })),
|
||||
results: ids.map((id) => ({ sessionId: id, status: 'deleted', message: 'Deleted by Browser Mock', backupPath: 'C:\\Users\\Mock\\.codex\\backups\\mock.zip' })),
|
||||
deletedCount: ids.length,
|
||||
failedCount: 0,
|
||||
};
|
||||
@@ -1167,15 +1167,15 @@ export function createBrowserMockRuntime(
|
||||
}
|
||||
case 'preview_codex_session_index_cleanup': return {
|
||||
snapshotSha256: 'mock-snapshot-sha256',
|
||||
candidates: [{ id: 'stale-thread-1', threadName: '已不存在的会话', updatedAt: isoHoursAgo(72) }],
|
||||
candidates: [{ id: 'stale-thread-1', threadName: 'Missing session', updatedAt: isoHoursAgo(72) }],
|
||||
};
|
||||
case 'apply_codex_session_index_cleanup': return { prunedEntries: asArray(asObject(payload.request).threadIds).length, backupPath: 'C:\\Users\\Mock\\.codex\\backups\\index.json' };
|
||||
|
||||
case 'get_lan_ipv4': return ['192.168.100.100'];
|
||||
default:
|
||||
if (command.startsWith('plugin:window|') || command.startsWith('plugin:webview|')) return null;
|
||||
console.warn(`[Browser Mock] 尚未实现命令:${command}`, rawPayload);
|
||||
throw new Error(`Browser Mock 尚未实现命令:${command}`);
|
||||
console.warn(`[Browser Mock] Command not implemented: ${command}`, rawPayload);
|
||||
throw new Error(`Browser Mock command not implemented: ${command}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ export function VersionManagementPage() {
|
||||
}
|
||||
|
||||
if (task.message && !task.running) {
|
||||
showNotice(task.message, task.phase === '安装失败' ? 'error' : 'info');
|
||||
showNotice(task.message, task.phase === 'Installation failed' ? 'error' : 'info');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -252,7 +252,7 @@ export function VersionManagementPage() {
|
||||
setProgress({
|
||||
running: true,
|
||||
cancellable: true,
|
||||
phase: '准备下载',
|
||||
phase: 'Preparing download',
|
||||
downloaded: 0,
|
||||
total: null,
|
||||
percent: null,
|
||||
@@ -267,7 +267,7 @@ export function VersionManagementPage() {
|
||||
setProgress({
|
||||
running: false,
|
||||
cancellable: false,
|
||||
phase: '安装完成',
|
||||
phase: 'Installation complete',
|
||||
downloaded: 1,
|
||||
total: 1,
|
||||
percent: 100,
|
||||
@@ -281,11 +281,12 @@ export function VersionManagementPage() {
|
||||
} catch (error) {
|
||||
manualInstallInProgressRef.current = false;
|
||||
const errorMessage = String(error);
|
||||
showNotice(errorMessage, errorMessage.includes('取消') ? 'info' : 'error');
|
||||
const cancelled = /cancel/i.test(errorMessage);
|
||||
showNotice(errorMessage, cancelled ? 'info' : 'error');
|
||||
setProgress((current) => ({
|
||||
running: false,
|
||||
cancellable: false,
|
||||
phase: errorMessage.includes('取消') ? '已取消' : '安装失败',
|
||||
phase: cancelled ? 'Canceled' : 'Installation failed',
|
||||
downloaded: current?.downloaded ?? 0,
|
||||
total: current?.total ?? null,
|
||||
percent: current?.percent ?? null,
|
||||
@@ -444,9 +445,9 @@ export function VersionManagementPage() {
|
||||
const progressKnown = computedPercent !== null;
|
||||
const progressPercent = clampPercent(computedPercent ?? 0);
|
||||
const progressText = progress
|
||||
? progress.phase === '安装完成'
|
||||
? progress.phase === 'Installation complete'
|
||||
? t('kernel.progress.completed')
|
||||
: progress.phase === '解压中'
|
||||
: progress.phase === 'Extracting'
|
||||
? t('kernel.progress.extracting')
|
||||
: progress.total
|
||||
? `${formatBytes(progress.downloaded)} / ${formatBytes(progress.total)}`
|
||||
@@ -457,7 +458,7 @@ export function VersionManagementPage() {
|
||||
|
||||
const installDialogTone: MessageType = progress?.result
|
||||
? 'success'
|
||||
: progress?.phase === '安装失败'
|
||||
: progress?.phase === 'Installation failed'
|
||||
? 'error'
|
||||
: 'info';
|
||||
|
||||
@@ -467,7 +468,7 @@ export function VersionManagementPage() {
|
||||
: t('kernel.install.titleInstalling')
|
||||
: progress?.result
|
||||
? t('kernel.install.titleCompleted')
|
||||
: progress?.phase === '已取消'
|
||||
: progress?.phase === 'Canceled'
|
||||
? t('kernel.install.titleCancelled')
|
||||
: t('kernel.install.titleFailed');
|
||||
|
||||
@@ -895,16 +896,21 @@ function localizeInstallPhase(
|
||||
t: ReturnType<typeof useI18n>['t'],
|
||||
) {
|
||||
const keys = {
|
||||
'准备下载': 'kernel.phase.preparingDownload',
|
||||
'下载中': 'kernel.phase.downloading',
|
||||
'解压中': 'kernel.phase.extracting',
|
||||
'准备内置内核': 'kernel.phase.preparingBundled',
|
||||
'安装完成': 'kernel.phase.completed',
|
||||
'安装失败': 'kernel.phase.failed',
|
||||
'已取消': 'kernel.phase.cancelled',
|
||||
'Preparing download': 'kernel.phase.preparingDownload',
|
||||
Downloading: 'kernel.phase.downloading',
|
||||
Extracting: 'kernel.phase.extracting',
|
||||
'Extract bundled kernel': 'kernel.phase.preparingBundled',
|
||||
'Installation complete': 'kernel.phase.completed',
|
||||
'Installation failed': 'kernel.phase.failed',
|
||||
Canceled: 'kernel.phase.cancelled',
|
||||
} as const;
|
||||
const key = keys[phase as keyof typeof keys];
|
||||
return key ? t(key) : phase;
|
||||
if (key) return t(key);
|
||||
if (phase === 'Preparing to install the latest version') {
|
||||
return t('kernel.install.installingLatest');
|
||||
}
|
||||
const version = phase.match(/^Preparing to install (.+)$/)?.[1];
|
||||
return version ? t('kernel.install.installingVersion', { version }) : phase;
|
||||
}
|
||||
|
||||
function clampPercent(percent: number) {
|
||||
|
||||
@@ -154,7 +154,10 @@ const formatDateTime = (date: Date): string | undefined => {
|
||||
}).format(date);
|
||||
};
|
||||
|
||||
export const formatQuotaTimestamp = (value: string | undefined, locale: AppLocale = 'zh-CN'): string => {
|
||||
export const formatQuotaTimestamp = (
|
||||
value: string | undefined,
|
||||
locale: AppLocale = getCurrentLocale(),
|
||||
): string => {
|
||||
if (!value) return '—';
|
||||
const ms = quotaResetInstant(value);
|
||||
if (ms === undefined) return '—';
|
||||
|
||||
+1
-1
@@ -9089,7 +9089,7 @@ textarea:focus-visible {
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
/* Events Tab (请求明细) */
|
||||
/* Events tab (request details) */
|
||||
.usage-events-panel {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
|
||||
Reference in new issue
Block a user