feat(i18n): Enhance internationalization support for tool specifications and notifications
- Added TitleKey and DescriptionKey fields to ToolSpec for dynamic localization. - Updated tool specifications to use i18nx for titles, descriptions, and appendices. - Refactored notification messages to utilize i18nx for localization in conversation and ticket assignment events. - Improved localization in the debug dialog component for skill definitions. - Added new translation keys in English and Chinese for ticket and conversation assignment notifications. - Ensured that fallback messages are properly localized based on user locale.
This commit is contained in:
@@ -457,10 +457,10 @@ function DebugDialogBody({
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-4">
|
||||
<ResultBlock title="Skill Route Trace" value={result?.skillRouteTrace} emptyText={t("skillDefinition.emptyData")} />
|
||||
<ResultBlock title="Tool Search Trace" value={result?.toolSearchTrace} emptyText={t("skillDefinition.emptyData")} />
|
||||
<ResultBlock title="Graph Tool Trace" value={result?.graphToolTrace} emptyText={t("skillDefinition.emptyData")} />
|
||||
<ResultBlock title="Trace Data" value={result?.traceData} emptyText={t("skillDefinition.emptyData")} />
|
||||
<ResultBlock title={t("skillDefinition.skillRouteTrace")} value={result?.skillRouteTrace} emptyText={t("skillDefinition.emptyData")} />
|
||||
<ResultBlock title={t("skillDefinition.toolSearchTrace")} value={result?.toolSearchTrace} emptyText={t("skillDefinition.emptyData")} />
|
||||
<ResultBlock title={t("skillDefinition.graphToolTrace")} value={result?.graphToolTrace} emptyText={t("skillDefinition.emptyData")} />
|
||||
<ResultBlock title={t("skillDefinition.traceData")} value={result?.traceData} emptyText={t("skillDefinition.emptyData")} />
|
||||
</div>
|
||||
|
||||
{result?.interrupted && result.checkPointId ? (
|
||||
@@ -555,9 +555,9 @@ function DebugDialogBody({
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<ResultBlock title="Resume Tool Search Trace" value={resumeResult.toolSearchTrace} emptyText={t("skillDefinition.emptyData")} />
|
||||
<ResultBlock title="Resume Graph Tool Trace" value={resumeResult.graphToolTrace} emptyText={t("skillDefinition.emptyData")} />
|
||||
<ResultBlock title="Resume Trace Data" value={resumeResult.traceData} emptyText={t("skillDefinition.emptyData")} />
|
||||
<ResultBlock title={t("skillDefinition.resumeToolSearchTrace")} value={resumeResult.toolSearchTrace} emptyText={t("skillDefinition.emptyData")} />
|
||||
<ResultBlock title={t("skillDefinition.resumeGraphToolTrace")} value={resumeResult.graphToolTrace} emptyText={t("skillDefinition.emptyData")} />
|
||||
<ResultBlock title={t("skillDefinition.resumeTraceData")} value={resumeResult.traceData} emptyText={t("skillDefinition.emptyData")} />
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { normalizeLocale } from "@/i18n/config"
|
||||
import { translateMessage } from "@/i18n/messages"
|
||||
|
||||
type LocalizableNotification = {
|
||||
title: string
|
||||
content: string
|
||||
@@ -19,76 +22,87 @@ export function localizeNotificationItem<T extends LocalizableNotification>(
|
||||
notification: T,
|
||||
locale: string
|
||||
): T {
|
||||
if (locale !== "en-US") {
|
||||
const normalizedLocale = normalizeLocale(locale)
|
||||
if (normalizedLocale !== "en-US") {
|
||||
return notification
|
||||
}
|
||||
if (notification.notificationType === "ticket_assigned") {
|
||||
return {
|
||||
...notification,
|
||||
title: localizeNotificationTitle(notification.title),
|
||||
content: localizeTicketAssignedContent(notification.content),
|
||||
title: localizeNotificationTitle(notification.title, normalizedLocale),
|
||||
content: localizeTicketAssignedContent(notification.content, normalizedLocale),
|
||||
}
|
||||
}
|
||||
if (notification.notificationType === "conversation_assigned") {
|
||||
return {
|
||||
...notification,
|
||||
title: localizeNotificationTitle(notification.title),
|
||||
content: localizeConversationAssignedContent(notification.content),
|
||||
title: localizeNotificationTitle(notification.title, normalizedLocale),
|
||||
content: localizeConversationAssignedContent(notification.content, normalizedLocale),
|
||||
}
|
||||
}
|
||||
return notification
|
||||
}
|
||||
|
||||
function localizeTicketAssignedContent(content: string) {
|
||||
function localizeTicketAssignedContent(content: string, locale: ReturnType<typeof normalizeLocale>) {
|
||||
const lines = splitNotificationLines(content)
|
||||
if (lines.length === 0) {
|
||||
return content
|
||||
}
|
||||
const match = lines[0].match(TICKET_ASSIGNED_PATTERN)
|
||||
if (match?.[1]) {
|
||||
lines[0] = `Ticket ${match[1]} has been assigned to you.`
|
||||
lines[0] = translateMessage(locale, "notification.ticketAssignedLine", {
|
||||
ticketNo: match[1],
|
||||
})
|
||||
}
|
||||
return lines
|
||||
.map((line) =>
|
||||
line.startsWith(ASSIGNMENT_REASON_PREFIX)
|
||||
? `Assignment reason: ${line.slice(ASSIGNMENT_REASON_PREFIX.length)}`
|
||||
? translateMessage(locale, "notification.assignmentReason", {
|
||||
reason: line.slice(ASSIGNMENT_REASON_PREFIX.length),
|
||||
})
|
||||
: line
|
||||
)
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
function localizeConversationAssignedContent(content: string) {
|
||||
function localizeConversationAssignedContent(content: string, locale: ReturnType<typeof normalizeLocale>) {
|
||||
const lines = splitNotificationLines(content)
|
||||
if (lines.length === 0) {
|
||||
return content
|
||||
}
|
||||
const match = lines[0].match(CONVERSATION_ASSIGNED_PATTERN)
|
||||
if (match?.[1]) {
|
||||
lines[0] = `Conversation #${match[1]} has been assigned to you.`
|
||||
lines[0] = translateMessage(locale, "notification.conversationAssignedLine", {
|
||||
conversationId: match[1],
|
||||
})
|
||||
}
|
||||
return lines
|
||||
.map((line) => {
|
||||
if (line.startsWith(CONVERSATION_ASSIGNMENT_REASON_PREFIX)) {
|
||||
return `Assignment reason: ${line.slice(CONVERSATION_ASSIGNMENT_REASON_PREFIX.length)}`
|
||||
return translateMessage(locale, "notification.assignmentReason", {
|
||||
reason: line.slice(CONVERSATION_ASSIGNMENT_REASON_PREFIX.length),
|
||||
})
|
||||
}
|
||||
if (line.startsWith(TRANSFER_REASON_PREFIX)) {
|
||||
return `Transfer reason: ${line.slice(TRANSFER_REASON_PREFIX.length)}`
|
||||
return translateMessage(locale, "notification.transferReason", {
|
||||
reason: line.slice(TRANSFER_REASON_PREFIX.length),
|
||||
})
|
||||
}
|
||||
return line
|
||||
})
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
function localizeNotificationTitle(title: string) {
|
||||
function localizeNotificationTitle(title: string, locale: ReturnType<typeof normalizeLocale>) {
|
||||
switch (title.trim()) {
|
||||
case TICKET_ASSIGNED_TITLE:
|
||||
return "Ticket assigned"
|
||||
return translateMessage(locale, "notification.ticketAssignedTitle")
|
||||
case CONVERSATION_TRANSFERRED_TITLE:
|
||||
return "Conversation transferred"
|
||||
return translateMessage(locale, "notification.conversationTransferredTitle")
|
||||
case CONVERSATION_AUTO_ASSIGNED_TITLE:
|
||||
return "Conversation auto-assigned"
|
||||
return translateMessage(locale, "notification.conversationAutoAssignedTitle")
|
||||
case CONVERSATION_ASSIGNED_TITLE:
|
||||
return "Conversation assigned"
|
||||
return translateMessage(locale, "notification.conversationAssignedTitle")
|
||||
default:
|
||||
return title
|
||||
}
|
||||
|
||||
+16
-1
@@ -53,7 +53,15 @@
|
||||
"markAllReadFailed": "Could not mark all notifications as read.",
|
||||
"fallbackTitle": "Notification",
|
||||
"loading": "Loading notifications",
|
||||
"empty": "No notifications"
|
||||
"empty": "No notifications",
|
||||
"ticketAssignedTitle": "Ticket assigned",
|
||||
"ticketAssignedLine": "Ticket {ticketNo} has been assigned to you.",
|
||||
"assignmentReason": "Assignment reason: {reason}",
|
||||
"conversationTransferredTitle": "Conversation transferred",
|
||||
"conversationAutoAssignedTitle": "Conversation auto-assigned",
|
||||
"conversationAssignedTitle": "Conversation assigned",
|
||||
"conversationAssignedLine": "Conversation #{conversationId} has been assigned to you.",
|
||||
"transferReason": "Transfer reason: {reason}"
|
||||
},
|
||||
"json": {
|
||||
"invalid": "Invalid JSON.",
|
||||
@@ -1643,6 +1651,13 @@
|
||||
"agentRequired": "Select an AI agent.",
|
||||
"messageRequired": "Enter a user message.",
|
||||
"emptyData": "No data",
|
||||
"skillRouteTrace": "Skill Route Trace",
|
||||
"toolSearchTrace": "Tool Search Trace",
|
||||
"graphToolTrace": "Graph Tool Trace",
|
||||
"traceData": "Trace Data",
|
||||
"resumeToolSearchTrace": "Resume Tool Search Trace",
|
||||
"resumeGraphToolTrace": "Resume Graph Tool Trace",
|
||||
"resumeTraceData": "Resume Trace Data",
|
||||
"confirm": "Confirm",
|
||||
"reject": "Cancel",
|
||||
"debugFailed": "Skill debug failed.",
|
||||
|
||||
+16
-1
@@ -53,7 +53,15 @@
|
||||
"markAllReadFailed": "全部已读失败",
|
||||
"fallbackTitle": "通知",
|
||||
"loading": "正在加载通知",
|
||||
"empty": "暂无通知"
|
||||
"empty": "暂无通知",
|
||||
"ticketAssignedTitle": "工单指派提醒",
|
||||
"ticketAssignedLine": "工单 {ticketNo} 已指派给你",
|
||||
"assignmentReason": "指派原因: {reason}",
|
||||
"conversationTransferredTitle": "会话转接提醒",
|
||||
"conversationAutoAssignedTitle": "会话自动分配提醒",
|
||||
"conversationAssignedTitle": "会话分配提醒",
|
||||
"conversationAssignedLine": "会话 #{conversationId} 已分配给你",
|
||||
"transferReason": "转接原因: {reason}"
|
||||
},
|
||||
"json": {
|
||||
"invalid": "JSON 格式不合法",
|
||||
@@ -1644,6 +1652,13 @@
|
||||
"agentRequired": "请选择 AI Agent",
|
||||
"messageRequired": "请输入用户消息",
|
||||
"emptyData": "暂无数据",
|
||||
"skillRouteTrace": "Skill 路由追踪",
|
||||
"toolSearchTrace": "工具搜索追踪",
|
||||
"graphToolTrace": "Graph 工具追踪",
|
||||
"traceData": "追踪数据",
|
||||
"resumeToolSearchTrace": "恢复工具搜索追踪",
|
||||
"resumeGraphToolTrace": "恢复 Graph 工具追踪",
|
||||
"resumeTraceData": "恢复追踪数据",
|
||||
"confirm": "确认",
|
||||
"reject": "取消",
|
||||
"debugFailed": "Skill 调试失败",
|
||||
|
||||
Reference in New Issue
Block a user