feat: 语言国际化重构
This commit is contained in:
@@ -9,6 +9,7 @@ import {
|
||||
parseSessionKey,
|
||||
} from '@runtime/lib/models';
|
||||
import type { ChatSession, RawMessage, ToolStatus } from '../shared/chat-model';
|
||||
import { t } from '../i18n';
|
||||
import { extractText, isToolOnlyMessage } from '../shared/chat-model';
|
||||
import { gatewayRpc, onGatewayEvent } from '../lib/gateway-client';
|
||||
import { hostApiFetch } from '../lib/host-api';
|
||||
@@ -202,7 +203,7 @@ function clearSessionEntryFromMap<T extends Record<string, unknown>>(entries: T,
|
||||
|
||||
function ensureSessionEntry(sessions: ChatSession[], sessionKey: string, displayName?: string): ChatSession[] {
|
||||
if (sessions.some((session) => session.key === sessionKey)) return sessions;
|
||||
return [...sessions, { key: sessionKey, displayName: displayName || 'New Chat' }];
|
||||
return [...sessions, { key: sessionKey, displayName: displayName || t('conversation.newConversation') }];
|
||||
}
|
||||
|
||||
function toMs(ts: number): number {
|
||||
@@ -386,7 +387,7 @@ async function loadSessions(): Promise<void> {
|
||||
|
||||
let sessions: ChatSession[] = normalizedKeys.map((key) => ({
|
||||
key,
|
||||
displayName: state.sessionLabels[key] || 'New Chat',
|
||||
displayName: state.sessionLabels[key] || t('conversation.newConversation'),
|
||||
updatedAt: state.sessionLastActivity[key] || Date.now(),
|
||||
}));
|
||||
|
||||
@@ -620,7 +621,7 @@ async function newSession(agentId = state.currentAgentId): Promise<void> {
|
||||
patchState({
|
||||
currentSessionKey: newKey,
|
||||
currentAgentId: 'local',
|
||||
sessions: [...nextSessions, { key: newKey, displayName: 'New Chat' }],
|
||||
sessions: [...nextSessions, { key: newKey, displayName: t('conversation.newConversation') }],
|
||||
sessionLabels: leavingEmpty
|
||||
? clearSessionEntryFromMap(state.sessionLabels, state.currentSessionKey)
|
||||
: state.sessionLabels,
|
||||
@@ -720,14 +721,14 @@ async function sendMessage(text: string, attachments: StagedAttachment[] = []):
|
||||
const targetAgentId = getAgentIdFromSessionKey(targetSessionKey);
|
||||
const providerAccountId = await resolveProviderAccountIdForAgent(targetAgentId);
|
||||
if (!providerAccountId) {
|
||||
patchState({ error: '请先为当前 Agent 配置可用模型,或先在 Models 页面设置默认模型' });
|
||||
patchState({ error: t('conversation.errors.modelUnavailable') });
|
||||
return false;
|
||||
}
|
||||
|
||||
const nowMs = Date.now();
|
||||
const userMessage: RawMessage = {
|
||||
role: 'user',
|
||||
content: trimmedText || (attachments.length > 0 ? '(file attached)' : ''),
|
||||
content: trimmedText || (attachments.length > 0 ? t('conversation.attachmentPlaceholder') : ''),
|
||||
timestamp: nowMs,
|
||||
id: crypto.randomUUID(),
|
||||
_attachedFiles: attachments.map((attachment) => ({
|
||||
@@ -739,7 +740,7 @@ async function sendMessage(text: string, attachments: StagedAttachment[] = []):
|
||||
})),
|
||||
};
|
||||
|
||||
const nextSessions = ensureSessionEntry(state.sessions, targetSessionKey, 'New Chat');
|
||||
const nextSessions = ensureSessionEntry(state.sessions, targetSessionKey, t('conversation.newConversation'));
|
||||
const isFirstUserMessage = !state.messages.some((message) => message.role === 'user');
|
||||
const nextLabels =
|
||||
!targetSessionKey.endsWith(':main') && isFirstUserMessage && trimmedText
|
||||
@@ -941,7 +942,7 @@ async function stageAttachmentFiles(files: File[]): Promise<StagedAttachment[]>
|
||||
for (const file of files) {
|
||||
const base64 = await new Promise<string>((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onerror = () => reject(reader.error || new Error('Failed to read file'));
|
||||
reader.onerror = () => reject(reader.error || new Error(t('conversation.errors.readFile')));
|
||||
reader.onloadend = () => {
|
||||
const dataUrl = String(reader.result || '');
|
||||
resolve(dataUrl.split(',')[1] || '');
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useSyncExternalStore } from 'react';
|
||||
import type { RoomTypeMapping } from '../api/types';
|
||||
import { t } from '../i18n';
|
||||
import type { SubTask, Task, TaskProgressPayload } from '../lib/task-types';
|
||||
import { CONFIG_KEYS, IPC_EVENTS } from '../lib/constants';
|
||||
import { invokeIpc, onIpc } from '../lib/host-api';
|
||||
@@ -73,13 +74,13 @@ function normalizeRoomList(roomList: RoomTypeMappingLike[]): RoomTypeMappingLike
|
||||
|
||||
function buildTaskSubTasks(taskId: string, roomType: RoomTypeMappingLike | undefined): SubTask[] {
|
||||
const scriptMappings = [
|
||||
{ prop: 'fzName', scriptId: 'fg_trace.js', name: '飞猪房态追踪', hasValue: Boolean(roomType?.fzName) },
|
||||
{ prop: 'mtName', scriptId: 'mt_trace.js', name: '美团房态追踪', hasValue: Boolean(roomType?.mtName) },
|
||||
{ prop: 'dyHotelName', scriptId: 'dy_hotel_trace.js', name: '抖音酒店房态追踪', hasValue: Boolean(roomType?.dyHotelName) },
|
||||
{ prop: 'fzName', scriptId: 'fg_trace.js', name: t('task.store.scriptNames.fliggyTrace'), hasValue: Boolean(roomType?.fzName) },
|
||||
{ prop: 'mtName', scriptId: 'mt_trace.js', name: t('task.store.scriptNames.meituanTrace'), hasValue: Boolean(roomType?.mtName) },
|
||||
{ prop: 'dyHotelName', scriptId: 'dy_hotel_trace.js', name: t('task.store.scriptNames.douyinHotelTrace'), hasValue: Boolean(roomType?.dyHotelName) },
|
||||
{
|
||||
prop: 'dyHotSpringName',
|
||||
scriptId: 'dy_hot_spring_trace.js',
|
||||
name: '抖音温泉房态追踪',
|
||||
name: t('task.store.scriptNames.douyinHotSpringTrace'),
|
||||
hasValue: Boolean(roomType?.dyHotSpringName || roomType?.dyHotSrpingName),
|
||||
},
|
||||
];
|
||||
@@ -93,7 +94,7 @@ function buildTaskSubTasks(taskId: string, roomType: RoomTypeMappingLike | undef
|
||||
name: mapping.name,
|
||||
status: 'pending' as const,
|
||||
progress: 0,
|
||||
message: '等待执行',
|
||||
message: t('task.store.messages.waiting'),
|
||||
stdoutTail: '',
|
||||
stderrTail: '',
|
||||
startedAt: new Date().toISOString(),
|
||||
@@ -219,7 +220,7 @@ async function initTaskStore(): Promise<void> {
|
||||
handleTaskProgress({
|
||||
...payload,
|
||||
progress: 0,
|
||||
message: '开始执行',
|
||||
message: t('task.store.messages.started'),
|
||||
});
|
||||
}) as (...args: any[]) => void);
|
||||
onIpc(IPC_EVENTS.TASK_COMPLETED, handleTaskCompleted as (...args: any[]) => void);
|
||||
@@ -233,10 +234,14 @@ function createTask(options: TaskOperationInput): Task {
|
||||
const roomList = normalizeRoomList(options.roomList);
|
||||
const roomType = roomList.find((item) => item.id === options.roomType);
|
||||
const subTasks = buildTaskSubTasks(taskId, roomType);
|
||||
const operationLabel = t(`task.operation.${options.operation}`);
|
||||
const roomName = roomType?.pmsName?.trim();
|
||||
|
||||
const task: Task = {
|
||||
id: taskId,
|
||||
title: `${options.operation === 'open' ? '开启' : '关闭'}渠道房型 - ${roomType?.pmsName || ''}`,
|
||||
title: roomName
|
||||
? t('task.store.taskTitleWithName', { operation: operationLabel, name: roomName })
|
||||
: t('task.store.taskTitleWithoutName', { operation: operationLabel }),
|
||||
operation: options.operation,
|
||||
roomType: options.roomType,
|
||||
dateRange: [options.startTime, options.endTime],
|
||||
@@ -254,7 +259,7 @@ function createTask(options: TaskOperationInput): Task {
|
||||
async function executeTask(taskId: string): Promise<ExecuteTaskResult> {
|
||||
const task = state.tasks.find((item) => item.id === taskId);
|
||||
if (!task) {
|
||||
return { success: false, error: '任务不存在,无法执行。' };
|
||||
return { success: false, error: t('task.store.errors.taskMissingExecute') };
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -268,7 +273,7 @@ async function executeTask(taskId: string): Promise<ExecuteTaskResult> {
|
||||
});
|
||||
|
||||
if (!result?.success) {
|
||||
markTaskFailed(task.id, result?.error || '任务执行失败');
|
||||
markTaskFailed(task.id, result?.error || t('task.store.errors.taskExecutionFailed'));
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -291,12 +296,12 @@ async function createAndExecuteTask(options: TaskOperationInput): Promise<{ task
|
||||
async function retryFailedSubTasks(taskId: string): Promise<ExecuteTaskResult> {
|
||||
const task = state.tasks.find((item) => item.id === taskId);
|
||||
if (!task) {
|
||||
return { success: false, error: '任务不存在,无法重试。' };
|
||||
return { success: false, error: t('task.store.errors.taskMissingRetry') };
|
||||
}
|
||||
|
||||
const hasFailedSubTask = task.subTasks.some((subTask) => subTask.status === 'failed');
|
||||
if (!hasFailedSubTask) {
|
||||
return { success: false, error: '当前任务没有可重试的失败子任务。' };
|
||||
return { success: false, error: t('task.store.errors.noFailedSubTasks') };
|
||||
}
|
||||
|
||||
updateTaskCollection((tasks) => tasks.map((currentTask) => {
|
||||
@@ -309,7 +314,7 @@ async function retryFailedSubTasks(taskId: string): Promise<ExecuteTaskResult> {
|
||||
...subTask,
|
||||
status: 'pending' as const,
|
||||
progress: 0,
|
||||
message: '等待执行',
|
||||
message: t('task.store.messages.waiting'),
|
||||
stdoutTail: '',
|
||||
stderrTail: '',
|
||||
error: undefined,
|
||||
|
||||
Reference in New Issue
Block a user