fix(i18n): replace hardcoded Chinese strings in Session Manager

- Add i18n keys for relative time (justNow, minutesAgo, hoursAgo, daysAgo)
- Add i18n keys for role labels (roleUser, roleSystem, roleTool)
- Add i18n keys for UI elements (tocTitle, searchSessions, clickToCopyPath)
- Update formatRelativeTime and getRoleLabel to accept t function
- Add useTranslation hook to SessionToc component
This commit is contained in:
Jason
2026-02-02 12:15:05 +08:00
parent 68a0c304d8
commit d98183f3da
8 changed files with 58 additions and 19 deletions
+1 -1
View File
@@ -70,7 +70,7 @@ export function SessionItem({
<div className="flex items-center gap-1 text-[11px] text-muted-foreground">
<Clock className="size-3" />
<span>
{lastActive ? formatRelativeTime(lastActive) : t("common.unknown")}
{lastActive ? formatRelativeTime(lastActive, t) : t("common.unknown")}
</span>
</div>
</button>
@@ -288,7 +288,7 @@ export function SessionManagerPage() {
<Search className="size-3.5" />
</Button>
</TooltipTrigger>
<TooltipContent></TooltipContent>
<TooltipContent>{t("sessionManager.searchSessions")}</TooltipContent>
</Tooltip>
<Select
@@ -482,7 +482,7 @@ export function SessionManagerPage() {
{selectedSession.projectDir}
</p>
<p className="text-muted-foreground mt-1">
{t("sessionManager.clickToCopyPath")}
</p>
</TooltipContent>
</Tooltip>
@@ -59,7 +59,7 @@ export function SessionMessageItem({
</Tooltip>
<div className="flex items-center justify-between text-xs mb-1.5 pr-6">
<span className={cn("font-semibold", getRoleTone(message.role))}>
{getRoleLabel(message.role)}
{getRoleLabel(message.role, t)}
</span>
{message.ts && (
<span className="text-muted-foreground">
+6 -3
View File
@@ -1,4 +1,5 @@
import { List, X } from "lucide-react";
import { useTranslation } from "react-i18next";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { ScrollArea } from "@/components/ui/scroll-area";
@@ -26,6 +27,7 @@ export function SessionTocSidebar({
items,
onItemClick,
}: SessionTocSidebarProps) {
const { t } = useTranslation();
if (items.length <= 2) return null;
return (
@@ -33,7 +35,7 @@ export function SessionTocSidebar({
<div className="p-3 border-b">
<div className="flex items-center gap-1.5 text-xs font-medium text-muted-foreground">
<List className="size-3.5" />
<span></span>
<span>{t("sessionManager.tocTitle")}</span>
</div>
</div>
<ScrollArea className="h-[calc(100%-40px)]">
@@ -74,6 +76,7 @@ export function SessionTocDialog({
open,
onOpenChange,
}: SessionTocDialogProps) {
const { t } = useTranslation();
if (items.length <= 2) return null;
return (
@@ -95,11 +98,11 @@ export function SessionTocDialog({
<DialogHeader className="px-4 py-3 relative border-b">
<DialogTitle className="flex items-center gap-2 text-base font-semibold">
<List className="size-4 text-primary" />
{t("sessionManager.tocTitle")}
</DialogTitle>
<DialogClose
className="absolute right-3 top-1/2 -translate-y-1/2 rounded-full p-1.5 hover:bg-muted transition-colors focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2"
aria-label="关闭"
aria-label={t("common.close")}
>
<X className="size-4 text-muted-foreground" />
</DialogClose>
+15 -9
View File
@@ -17,7 +17,10 @@ export const formatTimestamp = (value?: number) => {
return new Date(value).toLocaleString();
};
export const formatRelativeTime = (value?: number) => {
export const formatRelativeTime = (
value: number | undefined,
t: (key: string, options?: Record<string, unknown>) => string
) => {
if (!value) return "";
const now = Date.now();
const diff = now - value;
@@ -25,10 +28,10 @@ export const formatRelativeTime = (value?: number) => {
const hours = Math.floor(diff / 3600000);
const days = Math.floor(diff / 86400000);
if (minutes < 1) return "刚刚";
if (minutes < 60) return `${minutes} 分钟前`;
if (hours < 24) return `${hours} 小时前`;
if (days < 7) return `${days} 天前`;
if (minutes < 1) return t("sessionManager.justNow");
if (minutes < 60) return t("sessionManager.minutesAgo", { count: minutes });
if (hours < 24) return t("sessionManager.hoursAgo", { count: hours });
if (days < 7) return t("sessionManager.daysAgo", { count: days });
return new Date(value).toLocaleDateString();
};
@@ -57,12 +60,15 @@ export const getRoleTone = (role: string) => {
return "text-muted-foreground";
};
export const getRoleLabel = (role: string) => {
export const getRoleLabel = (
role: string,
t: (key: string) => string
) => {
const normalized = role.toLowerCase();
if (normalized === "assistant") return "AI";
if (normalized === "user") return "用户";
if (normalized === "system") return "系统";
if (normalized === "tool") return "工具";
if (normalized === "user") return t("sessionManager.roleUser");
if (normalized === "system") return t("sessionManager.roleSystem");
if (normalized === "tool") return t("sessionManager.roleTool");
return role;
};
+11 -1
View File
@@ -404,6 +404,7 @@
"title": "Session Manager",
"subtitle": "Manage Codex and Claude Code sessions",
"searchPlaceholder": "Search by content, directory, or ID",
"searchSessions": "Search sessions",
"providerFilterAll": "All",
"sessionList": "Sessions",
"loadingSessions": "Loading sessions...",
@@ -427,7 +428,16 @@
"copySourcePath": "Copy source file",
"sourcePathCopied": "Source file copied",
"loadingMessages": "Loading transcript...",
"emptySession": "No messages available"
"emptySession": "No messages available",
"clickToCopyPath": "Click to copy path",
"tocTitle": "Contents",
"justNow": "Just now",
"minutesAgo": "{{count}} min ago",
"hoursAgo": "{{count}} hr ago",
"daysAgo": "{{count}} days ago",
"roleUser": "User",
"roleSystem": "System",
"roleTool": "Tool"
},
"console": {
"providerSwitchReceived": "Received provider switch event:",
+11 -1
View File
@@ -404,6 +404,7 @@
"title": "セッション管理",
"subtitle": "Codex / Claude Code のセッションを管理",
"searchPlaceholder": "内容・ディレクトリ・ID で検索",
"searchSessions": "セッションを検索",
"providerFilterAll": "すべて",
"sessionList": "セッション一覧",
"loadingSessions": "セッションを読み込み中...",
@@ -427,7 +428,16 @@
"copySourcePath": "元ファイルをコピー",
"sourcePathCopied": "元ファイルをコピーしました",
"loadingMessages": "内容を読み込み中...",
"emptySession": "表示できる内容がありません"
"emptySession": "表示できる内容がありません",
"clickToCopyPath": "クリックしてパスをコピー",
"tocTitle": "目次",
"justNow": "たった今",
"minutesAgo": "{{count}}分前",
"hoursAgo": "{{count}}時間前",
"daysAgo": "{{count}}日前",
"roleUser": "ユーザー",
"roleSystem": "システム",
"roleTool": "ツール"
},
"console": {
"providerSwitchReceived": "プロバイダー切り替えイベントを受信:",
+11 -1
View File
@@ -404,6 +404,7 @@
"title": "会话管理",
"subtitle": "管理 Codex 与 Claude Code 会话记录",
"searchPlaceholder": "搜索会话内容、目录或 ID",
"searchSessions": "搜索会话",
"providerFilterAll": "全部",
"sessionList": "会话列表",
"loadingSessions": "加载会话中...",
@@ -427,7 +428,16 @@
"copySourcePath": "复制原始文件",
"sourcePathCopied": "原始文件已复制",
"loadingMessages": "加载会话内容中...",
"emptySession": "该会话暂无可展示内容"
"emptySession": "该会话暂无可展示内容",
"clickToCopyPath": "点击复制路径",
"tocTitle": "对话目录",
"justNow": "刚刚",
"minutesAgo": "{{count}} 分钟前",
"hoursAgo": "{{count}} 小时前",
"daysAgo": "{{count}} 天前",
"roleUser": "用户",
"roleSystem": "系统",
"roleTool": "工具"
},
"console": {
"providerSwitchReceived": "收到供应商切换事件:",