Sfoglia il codice sorgente

Translate history preview and view

Matt Rubens 9 mesi fa
parent
commit
6cdd10251a
27 ha cambiato i file con 594 aggiunte e 41 eliminazioni
  1. 1 1
      .roomodes
  2. 3 1
      webview-ui/src/components/history/CopyButton.tsx
  3. 6 6
      webview-ui/src/components/history/DeleteTaskDialog.tsx
  4. 18 13
      webview-ui/src/components/history/ExportButton.tsx
  5. 13 8
      webview-ui/src/components/history/HistoryPreview.tsx
  6. 14 12
      webview-ui/src/components/history/HistoryView.tsx
  7. 1 0
      webview-ui/src/components/history/__tests__/HistoryView.test.tsx
  8. 63 0
      webview-ui/src/i18n/__mocks__/TranslationContext.tsx
  9. 25 0
      webview-ui/src/i18n/locales/ar/history.json
  10. 25 0
      webview-ui/src/i18n/locales/ca/history.json
  11. 25 0
      webview-ui/src/i18n/locales/cs/history.json
  12. 25 0
      webview-ui/src/i18n/locales/de/history.json
  13. 25 0
      webview-ui/src/i18n/locales/en/history.json
  14. 25 0
      webview-ui/src/i18n/locales/es/history.json
  15. 25 0
      webview-ui/src/i18n/locales/fr/history.json
  16. 25 0
      webview-ui/src/i18n/locales/hi/history.json
  17. 25 0
      webview-ui/src/i18n/locales/hu/history.json
  18. 25 0
      webview-ui/src/i18n/locales/it/history.json
  19. 25 0
      webview-ui/src/i18n/locales/ja/history.json
  20. 25 0
      webview-ui/src/i18n/locales/ko/history.json
  21. 25 0
      webview-ui/src/i18n/locales/pl/history.json
  22. 25 0
      webview-ui/src/i18n/locales/pt-BR/history.json
  23. 25 0
      webview-ui/src/i18n/locales/pt/history.json
  24. 25 0
      webview-ui/src/i18n/locales/ru/history.json
  25. 25 0
      webview-ui/src/i18n/locales/tr/history.json
  26. 25 0
      webview-ui/src/i18n/locales/zh-CN/history.json
  27. 25 0
      webview-ui/src/i18n/locales/zh-TW/history.json

+ 1 - 1
.roomodes

@@ -22,7 +22,7 @@
       "slug": "translate",
       "slug": "translate",
       "name": "Translate",
       "name": "Translate",
       "roleDefinition": "You are Roo, a linguistic specialist focused on translating and managing localization files. Your responsibility is to help maintain and update translation files for the application, ensuring consistency and accuracy across all language resources.",
       "roleDefinition": "You are Roo, a linguistic specialist focused on translating and managing localization files. Your responsibility is to help maintain and update translation files for the application, ensuring consistency and accuracy across all language resources.",
-      "customInstructions": "When internationalizing and translating content:\n\n# Translation Style and Tone\n- Maintain a direct and concise style that mirrors the tone of the original text\n- Carefully account for colloquialisms and idiomatic expressions in both source and target languages\n- Aim for culturally relevant and meaningful translations rather than literal translations\n- Adapt the formality level to match the original content (whether formal or informal)\n- Preserve the personality and voice of the original content\n- Use natural-sounding language that feels native to speakers of the target language\n\n# Technical Implementation\n- Use namespaces to organize translations logically\n- Handle pluralization using i18next's built-in capabilities\n- Implement proper interpolation for variables using {{variable}} syntax\n- Don't include defaultValue. The `en` translations are the fallback.\n\n# Quality Assurance\n- Maintain consistent terminology across all translations\n- Respect the JSON structure of translation files\n- Watch for placeholders and preserve them in translations\n- Be mindful of text length in UI elements when translating to languages that might require more characters\n- Use context-aware translations when the same string has different meanings\n\n# Supported Languages\n- Localize all strings into the following locale files: ar, ca, cs, de, en, es, fr, hi, hu, it, ja, ko, pl, pt, pt-BR, ru, tr, zh-CN, zh-TW",
+      "customInstructions": "When internationalizing and translating content:\n\n# Translation Style and Tone\n- Maintain a direct and concise style that mirrors the tone of the original text\n- Carefully account for colloquialisms and idiomatic expressions in both source and target languages\n- Aim for culturally relevant and meaningful translations rather than literal translations\n- Adapt the formality level to match the original content (whether formal or informal)\n- Preserve the personality and voice of the original content\n- Use natural-sounding language that feels native to speakers of the target language\n- Don't translate the word \"token\" as it means something specific in English that all languages will understand\n\n# Technical Implementation\n- Use namespaces to organize translations logically\n- Handle pluralization using i18next's built-in capabilities\n- Implement proper interpolation for variables using {{variable}} syntax\n- Don't include defaultValue. The `en` translations are the fallback.\n\n# Quality Assurance\n- Maintain consistent terminology across all translations\n- Respect the JSON structure of translation files\n- Watch for placeholders and preserve them in translations\n- Be mindful of text length in UI elements when translating to languages that might require more characters\n- Use context-aware translations when the same string has different meanings\n\n# Supported Languages\n- Localize all strings into the following locale files: ar, ca, cs, de, en, es, fr, hi, hu, it, ja, ko, pl, pt, pt-BR, ru, tr, zh-CN, zh-TW",
       "groups": [
       "groups": [
         "read",
         "read",
         [
         [

+ 3 - 1
webview-ui/src/components/history/CopyButton.tsx

@@ -3,6 +3,7 @@ import { useCallback } from "react"
 import { useClipboard } from "@/components/ui/hooks"
 import { useClipboard } from "@/components/ui/hooks"
 import { Button } from "@/components/ui"
 import { Button } from "@/components/ui"
 import { cn } from "@/lib/utils"
 import { cn } from "@/lib/utils"
+import { useAppTranslation } from "@/i18n/TranslationContext"
 
 
 type CopyButtonProps = {
 type CopyButtonProps = {
 	itemTask: string
 	itemTask: string
@@ -10,6 +11,7 @@ type CopyButtonProps = {
 
 
 export const CopyButton = ({ itemTask }: CopyButtonProps) => {
 export const CopyButton = ({ itemTask }: CopyButtonProps) => {
 	const { isCopied, copy } = useClipboard()
 	const { isCopied, copy } = useClipboard()
+	const { t } = useAppTranslation()
 
 
 	const onCopy = useCallback(
 	const onCopy = useCallback(
 		(e: React.MouseEvent) => {
 		(e: React.MouseEvent) => {
@@ -23,7 +25,7 @@ export const CopyButton = ({ itemTask }: CopyButtonProps) => {
 		<Button
 		<Button
 			variant="ghost"
 			variant="ghost"
 			size="icon"
 			size="icon"
-			title="Copy Prompt"
+			title={t("history:copyPrompt")}
 			onClick={onCopy}
 			onClick={onCopy}
 			className="opacity-50 hover:opacity-100">
 			className="opacity-50 hover:opacity-100">
 			<span className={cn("codicon scale-80", { "codicon-check": isCopied, "codicon-copy": !isCopied })} />
 			<span className={cn("codicon scale-80", { "codicon-check": isCopied, "codicon-copy": !isCopied })} />

+ 6 - 6
webview-ui/src/components/history/DeleteTaskDialog.tsx

@@ -13,6 +13,7 @@ import {
 	AlertDialogTitle,
 	AlertDialogTitle,
 	Button,
 	Button,
 } from "@/components/ui"
 } from "@/components/ui"
+import { useAppTranslation } from "@/i18n/TranslationContext"
 
 
 import { vscode } from "@/utils/vscode"
 import { vscode } from "@/utils/vscode"
 
 
@@ -21,6 +22,7 @@ interface DeleteTaskDialogProps extends AlertDialogProps {
 }
 }
 
 
 export const DeleteTaskDialog = ({ taskId, ...props }: DeleteTaskDialogProps) => {
 export const DeleteTaskDialog = ({ taskId, ...props }: DeleteTaskDialogProps) => {
+	const { t } = useAppTranslation()
 	const [isEnterPressed] = useKeyPress("Enter")
 	const [isEnterPressed] = useKeyPress("Enter")
 
 
 	const { onOpenChange } = props
 	const { onOpenChange } = props
@@ -42,18 +44,16 @@ export const DeleteTaskDialog = ({ taskId, ...props }: DeleteTaskDialogProps) =>
 		<AlertDialog {...props}>
 		<AlertDialog {...props}>
 			<AlertDialogContent onEscapeKeyDown={() => onOpenChange?.(false)}>
 			<AlertDialogContent onEscapeKeyDown={() => onOpenChange?.(false)}>
 				<AlertDialogHeader>
 				<AlertDialogHeader>
-					<AlertDialogTitle>Delete Task</AlertDialogTitle>
-					<AlertDialogDescription>
-						Are you sure you want to delete this task? This action cannot be undone.
-					</AlertDialogDescription>
+					<AlertDialogTitle>{t("history:deleteTask")}</AlertDialogTitle>
+					<AlertDialogDescription>{t("history:deleteTaskMessage")}</AlertDialogDescription>
 				</AlertDialogHeader>
 				</AlertDialogHeader>
 				<AlertDialogFooter>
 				<AlertDialogFooter>
 					<AlertDialogCancel asChild>
 					<AlertDialogCancel asChild>
-						<Button variant="secondary">Cancel</Button>
+						<Button variant="secondary">{t("history:cancel")}</Button>
 					</AlertDialogCancel>
 					</AlertDialogCancel>
 					<AlertDialogAction asChild>
 					<AlertDialogAction asChild>
 						<Button variant="destructive" onClick={onDelete}>
 						<Button variant="destructive" onClick={onDelete}>
-							Delete
+							{t("history:delete")}
 						</Button>
 						</Button>
 					</AlertDialogAction>
 					</AlertDialogAction>
 				</AlertDialogFooter>
 				</AlertDialogFooter>

+ 18 - 13
webview-ui/src/components/history/ExportButton.tsx

@@ -1,16 +1,21 @@
 import { vscode } from "@/utils/vscode"
 import { vscode } from "@/utils/vscode"
 import { Button } from "@/components/ui"
 import { Button } from "@/components/ui"
+import { useAppTranslation } from "@/i18n/TranslationContext"
 
 
-export const ExportButton = ({ itemId }: { itemId: string }) => (
-	<Button
-		data-testid="export"
-		variant="ghost"
-		size="icon"
-		title="Export Task"
-		onClick={(e) => {
-			e.stopPropagation()
-			vscode.postMessage({ type: "exportTaskWithId", text: itemId })
-		}}>
-		<span className="codicon codicon-cloud-download" />
-	</Button>
-)
+export const ExportButton = ({ itemId }: { itemId: string }) => {
+	const { t } = useAppTranslation()
+
+	return (
+		<Button
+			data-testid="export"
+			variant="ghost"
+			size="icon"
+			title={t("history:exportTask")}
+			onClick={(e) => {
+				e.stopPropagation()
+				vscode.postMessage({ type: "exportTaskWithId", text: itemId })
+			}}>
+			<span className="codicon codicon-cloud-download" />
+		</Button>
+	)
+}

+ 13 - 8
webview-ui/src/components/history/HistoryPreview.tsx

@@ -5,24 +5,25 @@ import { formatLargeNumber, formatDate } from "@/utils/format"
 import { Button } from "@/components/ui"
 import { Button } from "@/components/ui"
 
 
 import { useExtensionState } from "../../context/ExtensionStateContext"
 import { useExtensionState } from "../../context/ExtensionStateContext"
+import { useAppTranslation } from "../../i18n/TranslationContext"
 import { CopyButton } from "./CopyButton"
 import { CopyButton } from "./CopyButton"
 
 
 type HistoryPreviewProps = {
 type HistoryPreviewProps = {
 	showHistoryView: () => void
 	showHistoryView: () => void
 }
 }
-
 const HistoryPreview = ({ showHistoryView }: HistoryPreviewProps) => {
 const HistoryPreview = ({ showHistoryView }: HistoryPreviewProps) => {
 	const { taskHistory } = useExtensionState()
 	const { taskHistory } = useExtensionState()
+	const { t } = useAppTranslation()
 
 
 	return (
 	return (
 		<div className="flex flex-col gap-3 shrink-0 mx-5">
 		<div className="flex flex-col gap-3 shrink-0 mx-5">
 			<div className="flex items-center justify-between text-vscode-descriptionForeground">
 			<div className="flex items-center justify-between text-vscode-descriptionForeground">
 				<div className="flex items-center gap-1">
 				<div className="flex items-center gap-1">
 					<span className="codicon codicon-comment-discussion scale-90 mr-1" />
 					<span className="codicon codicon-comment-discussion scale-90 mr-1" />
-					<span className="font-medium text-xs uppercase">Recent Tasks</span>
+					<span className="font-medium text-xs uppercase">{t("history:recentTasks")}</span>
 				</div>
 				</div>
 				<Button variant="ghost" size="sm" onClick={() => showHistoryView()} className="uppercase">
 				<Button variant="ghost" size="sm" onClick={() => showHistoryView()} className="uppercase">
-					View All
+					{t("history:viewAll")}
 				</Button>
 				</Button>
 			</div>
 			</div>
 			{taskHistory.slice(0, 3).map((item) => (
 			{taskHistory.slice(0, 3).map((item) => (
@@ -50,22 +51,26 @@ const HistoryPreview = ({ showHistoryView }: HistoryPreviewProps) => {
 						</div>
 						</div>
 						<div className="text-xs text-vscode-descriptionForeground">
 						<div className="text-xs text-vscode-descriptionForeground">
 							<span>
 							<span>
-								Tokens: ↑{formatLargeNumber(item.tokensIn || 0)} ↓
-								{formatLargeNumber(item.tokensOut || 0)}
+								{t("history:tokens", {
+									in: formatLargeNumber(item.tokensIn || 0),
+									out: formatLargeNumber(item.tokensOut || 0),
+								})}
 							</span>
 							</span>
 							{!!item.cacheWrites && (
 							{!!item.cacheWrites && (
 								<>
 								<>
 									{" • "}
 									{" • "}
 									<span>
 									<span>
-										Cache: +{formatLargeNumber(item.cacheWrites || 0)} →{" "}
-										{formatLargeNumber(item.cacheReads || 0)}
+										{t("history:cache", {
+											writes: formatLargeNumber(item.cacheWrites || 0),
+											reads: formatLargeNumber(item.cacheReads || 0),
+										})}
 									</span>
 									</span>
 								</>
 								</>
 							)}
 							)}
 							{!!item.totalCost && (
 							{!!item.totalCost && (
 								<>
 								<>
 									{" • "}
 									{" • "}
-									<span>API Cost: ${item.totalCost?.toFixed(4)}</span>
+									<span>{t("history:apiCost", { cost: item.totalCost?.toFixed(4) })}</span>
 								</>
 								</>
 							)}
 							)}
 						</div>
 						</div>

+ 14 - 12
webview-ui/src/components/history/HistoryView.tsx

@@ -8,6 +8,7 @@ import { vscode } from "@/utils/vscode"
 import { formatLargeNumber, formatDate } from "@/utils/format"
 import { formatLargeNumber, formatDate } from "@/utils/format"
 import { cn } from "@/lib/utils"
 import { cn } from "@/lib/utils"
 import { Button } from "@/components/ui"
 import { Button } from "@/components/ui"
+import { useAppTranslation } from "@/i18n/TranslationContext"
 
 
 import { Tab, TabContent, TabHeader } from "../common/Tab"
 import { Tab, TabContent, TabHeader } from "../common/Tab"
 import { useTaskSearch } from "./useTaskSearch"
 import { useTaskSearch } from "./useTaskSearch"
@@ -22,6 +23,7 @@ type SortOption = "newest" | "oldest" | "mostExpensive" | "mostTokens" | "mostRe
 
 
 const HistoryView = ({ onDone }: HistoryViewProps) => {
 const HistoryView = ({ onDone }: HistoryViewProps) => {
 	const { tasks, searchQuery, setSearchQuery, sortOption, setSortOption, setLastNonRelevantSort } = useTaskSearch()
 	const { tasks, searchQuery, setSearchQuery, sortOption, setSortOption, setLastNonRelevantSort } = useTaskSearch()
+	const { t } = useAppTranslation()
 
 
 	const [deleteTaskId, setDeleteTaskId] = useState<string | null>(null)
 	const [deleteTaskId, setDeleteTaskId] = useState<string | null>(null)
 
 
@@ -29,13 +31,13 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
 		<Tab>
 		<Tab>
 			<TabHeader className="flex flex-col gap-2">
 			<TabHeader className="flex flex-col gap-2">
 				<div className="flex justify-between items-center">
 				<div className="flex justify-between items-center">
-					<h3 className="text-vscode-foreground m-0">History</h3>
-					<VSCodeButton onClick={onDone}>Done</VSCodeButton>
+					<h3 className="text-vscode-foreground m-0">{t("history:history")}</h3>
+					<VSCodeButton onClick={onDone}>{t("history:done")}</VSCodeButton>
 				</div>
 				</div>
 				<div className="flex flex-col gap-2">
 				<div className="flex flex-col gap-2">
 					<VSCodeTextField
 					<VSCodeTextField
 						style={{ width: "100%" }}
 						style={{ width: "100%" }}
-						placeholder="Fuzzy search history..."
+						placeholder={t("history:searchPlaceholder")}
 						value={searchQuery}
 						value={searchQuery}
 						onInput={(e) => {
 						onInput={(e) => {
 							const newValue = (e.target as HTMLInputElement)?.value
 							const newValue = (e.target as HTMLInputElement)?.value
@@ -70,15 +72,15 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
 						value={sortOption}
 						value={sortOption}
 						role="radiogroup"
 						role="radiogroup"
 						onChange={(e) => setSortOption((e.target as HTMLInputElement).value as SortOption)}>
 						onChange={(e) => setSortOption((e.target as HTMLInputElement).value as SortOption)}>
-						<VSCodeRadio value="newest">Newest</VSCodeRadio>
-						<VSCodeRadio value="oldest">Oldest</VSCodeRadio>
-						<VSCodeRadio value="mostExpensive">Most Expensive</VSCodeRadio>
-						<VSCodeRadio value="mostTokens">Most Tokens</VSCodeRadio>
+						<VSCodeRadio value="newest">{t("history:newest")}</VSCodeRadio>
+						<VSCodeRadio value="oldest">{t("history:oldest")}</VSCodeRadio>
+						<VSCodeRadio value="mostExpensive">{t("history:mostExpensive")}</VSCodeRadio>
+						<VSCodeRadio value="mostTokens">{t("history:mostTokens")}</VSCodeRadio>
 						<VSCodeRadio
 						<VSCodeRadio
 							value="mostRelevant"
 							value="mostRelevant"
 							disabled={!searchQuery}
 							disabled={!searchQuery}
 							style={{ opacity: searchQuery ? 1 : 0.5 }}>
 							style={{ opacity: searchQuery ? 1 : 0.5 }}>
-							Most Relevant
+							{t("history:mostRelevant")}
 						</VSCodeRadio>
 						</VSCodeRadio>
 					</VSCodeRadioGroup>
 					</VSCodeRadioGroup>
 				</div>
 				</div>
@@ -132,7 +134,7 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
 										<Button
 										<Button
 											variant="ghost"
 											variant="ghost"
 											size="sm"
 											size="sm"
-											title="Delete Task (Shift + Click to skip confirmation)"
+											title={t("history:deleteTaskTitle")}
 											onClick={(e) => {
 											onClick={(e) => {
 												e.stopPropagation()
 												e.stopPropagation()
 
 
@@ -181,7 +183,7 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
 													fontWeight: 500,
 													fontWeight: 500,
 													color: "var(--vscode-descriptionForeground)",
 													color: "var(--vscode-descriptionForeground)",
 												}}>
 												}}>
-												Tokens:
+												{t("history:tokensLabel")}
 											</span>
 											</span>
 											<span
 											<span
 												data-testid="tokens-in"
 												data-testid="tokens-in"
@@ -242,7 +244,7 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
 													fontWeight: 500,
 													fontWeight: 500,
 													color: "var(--vscode-descriptionForeground)",
 													color: "var(--vscode-descriptionForeground)",
 												}}>
 												}}>
-												Cache:
+												{t("history:cacheLabel")}
 											</span>
 											</span>
 											<span
 											<span
 												data-testid="cache-writes"
 												data-testid="cache-writes"
@@ -297,7 +299,7 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
 														fontWeight: 500,
 														fontWeight: 500,
 														color: "var(--vscode-descriptionForeground)",
 														color: "var(--vscode-descriptionForeground)",
 													}}>
 													}}>
-													API Cost:
+													{t("history:apiCostLabel")}
 												</span>
 												</span>
 												<span style={{ color: "var(--vscode-descriptionForeground)" }}>
 												<span style={{ color: "var(--vscode-descriptionForeground)" }}>
 													${item.totalCost?.toFixed(4)}
 													${item.totalCost?.toFixed(4)}

+ 1 - 0
webview-ui/src/components/history/__tests__/HistoryView.test.tsx

@@ -7,6 +7,7 @@ import { vscode } from "../../../utils/vscode"
 
 
 jest.mock("../../../context/ExtensionStateContext")
 jest.mock("../../../context/ExtensionStateContext")
 jest.mock("../../../utils/vscode")
 jest.mock("../../../utils/vscode")
+jest.mock("../../../i18n/TranslationContext")
 
 
 jest.mock("react-virtuoso", () => ({
 jest.mock("react-virtuoso", () => ({
 	Virtuoso: ({ data, itemContent }: any) => (
 	Virtuoso: ({ data, itemContent }: any) => (

+ 63 - 0
webview-ui/src/i18n/__mocks__/TranslationContext.tsx

@@ -0,0 +1,63 @@
+import React from "react"
+
+// Create a mock for the useAppTranslation hook
+export const useAppTranslation = () => {
+	return {
+		t: (key: string, options?: Record<string, any>) => {
+			const translations: Record<string, string> = {
+				// History translations
+				"history:recentTasks": "Recent Tasks",
+				"history:viewAll": "View All",
+				"history:history": "History",
+				"history:done": "Done",
+				"history:searchPlaceholder": "Fuzzy search history...",
+				"history:newest": "Newest",
+				"history:oldest": "Oldest",
+				"history:mostExpensive": "Most Expensive",
+				"history:mostTokens": "Most Tokens",
+				"history:mostRelevant": "Most Relevant",
+				"history:deleteTaskTitle": "Delete Task (Shift + Click to skip confirmation)",
+				"history:tokensLabel": "Tokens:",
+				"history:cacheLabel": "Cache:",
+				"history:apiCostLabel": "API Cost:",
+				"history:copyPrompt": "Copy Prompt",
+				"history:exportTask": "Export Task",
+				"history:deleteTask": "Delete Task",
+				"history:deleteTaskMessage": "Are you sure you want to delete this task? This action cannot be undone.",
+				"history:cancel": "Cancel",
+				"history:delete": "Delete",
+			}
+
+			// Handle interpolation
+			if (options && key === "history:tokens") {
+				return `Tokens: ↑${options.in} ↓${options.out}`
+			}
+
+			if (options && key === "history:cache") {
+				return `Cache: +${options.writes} → ${options.reads}`
+			}
+
+			if (options && key === "history:apiCost") {
+				return `API Cost: $${options.cost}`
+			}
+
+			return translations[key] || key
+		},
+		i18n: {
+			language: "en",
+			changeLanguage: jest.fn(),
+		},
+	}
+}
+
+export const withTranslation = (Component: React.ComponentType<any>) => {
+	return (props: any) => <Component {...props} />
+}
+
+// Mock provider component
+export const AppTranslationProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
+	return <>{children}</>
+}
+
+const TranslationContext = { AppTranslationProvider, useAppTranslation, withTranslation }
+export default TranslationContext

+ 25 - 0
webview-ui/src/i18n/locales/ar/history.json

@@ -0,0 +1,25 @@
+{
+	"recentTasks": "المهام الأخيرة",
+	"viewAll": "عرض الكل",
+	"tokens": "الرموز: ↑{{in}} ↓{{out}}",
+	"cache": "التخزين المؤقت: +{{writes}} → {{reads}}",
+	"apiCost": "تكلفة API: ${{cost}}",
+	"history": "السجل",
+	"done": "تم",
+	"searchPlaceholder": "البحث في السجل...",
+	"newest": "الأحدث",
+	"oldest": "الأقدم",
+	"mostExpensive": "الأكثر تكلفة",
+	"mostTokens": "الأكثر رموزًا",
+	"mostRelevant": "الأكثر صلة",
+	"deleteTaskTitle": "حذف المهمة (Shift + نقرة لتخطي التأكيد)",
+	"tokensLabel": "الرموز:",
+	"cacheLabel": "التخزين المؤقت:",
+	"apiCostLabel": "تكلفة API:",
+	"copyPrompt": "نسخ السؤال",
+	"exportTask": "تصدير المهمة",
+	"deleteTask": "حذف المهمة",
+	"deleteTaskMessage": "هل أنت متأكد من حذف هذه المهمة؟ لا يمكن التراجع عن هذا الإجراء.",
+	"cancel": "إلغاء",
+	"delete": "حذف"
+}

+ 25 - 0
webview-ui/src/i18n/locales/ca/history.json

@@ -0,0 +1,25 @@
+{
+	"recentTasks": "Tasques recents",
+	"viewAll": "Veure tot",
+	"tokens": "Tokens: ↑{{in}} ↓{{out}}",
+	"cache": "Cau: +{{writes}} → {{reads}}",
+	"apiCost": "Cost d'API: ${{cost}}",
+	"history": "Historial",
+	"done": "Fet",
+	"searchPlaceholder": "Cerca a l'historial...",
+	"newest": "Més recents",
+	"oldest": "Més antigues",
+	"mostExpensive": "Més cares",
+	"mostTokens": "Més tokens",
+	"mostRelevant": "Més rellevants",
+	"deleteTaskTitle": "Eliminar tasca (Maj + Clic per ometre confirmació)",
+	"tokensLabel": "Tokens:",
+	"cacheLabel": "Cau:",
+	"apiCostLabel": "Cost d'API:",
+	"copyPrompt": "Copiar prompt",
+	"exportTask": "Exportar tasca",
+	"deleteTask": "Eliminar tasca",
+	"deleteTaskMessage": "Estàs segur que vols eliminar aquesta tasca? Aquesta acció no es pot desfer.",
+	"cancel": "Cancel·lar",
+	"delete": "Eliminar"
+}

+ 25 - 0
webview-ui/src/i18n/locales/cs/history.json

@@ -0,0 +1,25 @@
+{
+	"recentTasks": "Nedávné úkoly",
+	"viewAll": "Zobrazit vše",
+	"tokens": "Tokens: ↑{{in}} ↓{{out}}",
+	"cache": "Mezipaměť: +{{writes}} → {{reads}}",
+	"apiCost": "Náklady API: ${{cost}}",
+	"history": "Historie",
+	"done": "Hotovo",
+	"searchPlaceholder": "Vyhledat v historii...",
+	"newest": "Nejnovější",
+	"oldest": "Nejstarší",
+	"mostExpensive": "Nejdražší",
+	"mostTokens": "Nejvíce tokenů",
+	"mostRelevant": "Nejrelevantnější",
+	"deleteTaskTitle": "Smazat úkol (Shift + klik pro přeskočení potvrzení)",
+	"tokensLabel": "Tokens:",
+	"cacheLabel": "Mezipaměť:",
+	"apiCostLabel": "Náklady API:",
+	"copyPrompt": "Kopírovat prompt",
+	"exportTask": "Exportovat úkol",
+	"deleteTask": "Smazat úkol",
+	"deleteTaskMessage": "Opravdu chcete smazat tento úkol? Tuto akci nelze vrátit zpět.",
+	"cancel": "Zrušit",
+	"delete": "Smazat"
+}

+ 25 - 0
webview-ui/src/i18n/locales/de/history.json

@@ -0,0 +1,25 @@
+{
+	"recentTasks": "Neueste Aufgaben",
+	"viewAll": "Alle anzeigen",
+	"tokens": "Tokens: ↑{{in}} ↓{{out}}",
+	"cache": "Cache: +{{writes}} → {{reads}}",
+	"apiCost": "API-Kosten: ${{cost}}",
+	"history": "Verlauf",
+	"done": "Fertig",
+	"searchPlaceholder": "Verlauf durchsuchen...",
+	"newest": "Neueste",
+	"oldest": "Älteste",
+	"mostExpensive": "Teuerste",
+	"mostTokens": "Meiste Tokens",
+	"mostRelevant": "Relevanteste",
+	"deleteTaskTitle": "Aufgabe löschen (Umschalt + Klick um Bestätigung zu überspringen)",
+	"tokensLabel": "Tokens:",
+	"cacheLabel": "Cache:",
+	"apiCostLabel": "API-Kosten:",
+	"copyPrompt": "Prompt kopieren",
+	"exportTask": "Aufgabe exportieren",
+	"deleteTask": "Aufgabe löschen",
+	"deleteTaskMessage": "Sind Sie sicher, dass Sie diese Aufgabe löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.",
+	"cancel": "Abbrechen",
+	"delete": "Löschen"
+}

+ 25 - 0
webview-ui/src/i18n/locales/en/history.json

@@ -0,0 +1,25 @@
+{
+	"recentTasks": "Recent Tasks",
+	"viewAll": "View All",
+	"tokens": "Tokens: ↑{{in}} ↓{{out}}",
+	"cache": "Cache: +{{writes}} → {{reads}}",
+	"apiCost": "API Cost: ${{cost}}",
+	"history": "History",
+	"done": "Done",
+	"searchPlaceholder": "Fuzzy search history...",
+	"newest": "Newest",
+	"oldest": "Oldest",
+	"mostExpensive": "Most Expensive",
+	"mostTokens": "Most Tokens",
+	"mostRelevant": "Most Relevant",
+	"deleteTaskTitle": "Delete Task (Shift + Click to skip confirmation)",
+	"tokensLabel": "Tokens:",
+	"cacheLabel": "Cache:",
+	"apiCostLabel": "API Cost:",
+	"copyPrompt": "Copy Prompt",
+	"exportTask": "Export Task",
+	"deleteTask": "Delete Task",
+	"deleteTaskMessage": "Are you sure you want to delete this task? This action cannot be undone.",
+	"cancel": "Cancel",
+	"delete": "Delete"
+}

+ 25 - 0
webview-ui/src/i18n/locales/es/history.json

@@ -0,0 +1,25 @@
+{
+	"recentTasks": "Tareas recientes",
+	"viewAll": "Ver todas",
+	"tokens": "Tokens: ↑{{in}} ↓{{out}}",
+	"cache": "Caché: +{{writes}} → {{reads}}",
+	"apiCost": "Costo de API: ${{cost}}",
+	"history": "Historial",
+	"done": "Listo",
+	"searchPlaceholder": "Buscar en el historial...",
+	"newest": "Más recientes",
+	"oldest": "Más antiguas",
+	"mostExpensive": "Más costosas",
+	"mostTokens": "Más tokens",
+	"mostRelevant": "Más relevantes",
+	"deleteTaskTitle": "Eliminar tarea (Shift + Clic para omitir confirmación)",
+	"tokensLabel": "Tokens:",
+	"cacheLabel": "Caché:",
+	"apiCostLabel": "Costo de API:",
+	"copyPrompt": "Copiar prompt",
+	"exportTask": "Exportar tarea",
+	"deleteTask": "Eliminar tarea",
+	"deleteTaskMessage": "¿Estás seguro de que quieres eliminar esta tarea? Esta acción no se puede deshacer.",
+	"cancel": "Cancelar",
+	"delete": "Eliminar"
+}

+ 25 - 0
webview-ui/src/i18n/locales/fr/history.json

@@ -0,0 +1,25 @@
+{
+	"recentTasks": "Tâches récentes",
+	"viewAll": "Voir tout",
+	"tokens": "Tokens: ↑{{in}} ↓{{out}}",
+	"cache": "Cache: +{{writes}} → {{reads}}",
+	"apiCost": "Coût API: ${{cost}}",
+	"history": "Historique",
+	"done": "Terminé",
+	"searchPlaceholder": "Rechercher dans l'historique...",
+	"newest": "Plus récentes",
+	"oldest": "Plus anciennes",
+	"mostExpensive": "Plus coûteuses",
+	"mostTokens": "Plus de tokens",
+	"mostRelevant": "Plus pertinentes",
+	"deleteTaskTitle": "Supprimer la tâche (Maj + Clic pour ignorer la confirmation)",
+	"tokensLabel": "Tokens:",
+	"cacheLabel": "Cache:",
+	"apiCostLabel": "Coût API:",
+	"copyPrompt": "Copier le prompt",
+	"exportTask": "Exporter la tâche",
+	"deleteTask": "Supprimer la tâche",
+	"deleteTaskMessage": "Êtes-vous sûr de vouloir supprimer cette tâche ? Cette action ne peut pas être annulée.",
+	"cancel": "Annuler",
+	"delete": "Supprimer"
+}

+ 25 - 0
webview-ui/src/i18n/locales/hi/history.json

@@ -0,0 +1,25 @@
+{
+	"recentTasks": "हालिया कार्य",
+	"viewAll": "सभी देखें",
+	"tokens": "Tokens: ↑{{in}} ↓{{out}}",
+	"cache": "कैश: +{{writes}} → {{reads}}",
+	"apiCost": "API लागत: ${{cost}}",
+	"history": "इतिहास",
+	"done": "पूर्ण",
+	"searchPlaceholder": "इतिहास खोजें...",
+	"newest": "नवीनतम",
+	"oldest": "सबसे पुराना",
+	"mostExpensive": "सबसे महंगा",
+	"mostTokens": "सबसे अधिक टोकन",
+	"mostRelevant": "सबसे प्रासंगिक",
+	"deleteTaskTitle": "कार्य हटाएं (Shift + क्लिक पुष्टि छोड़ने के लिए)",
+	"tokensLabel": "Tokens:",
+	"cacheLabel": "कैश:",
+	"apiCostLabel": "API लागत:",
+	"copyPrompt": "प्रॉम्प्ट कॉपी करें",
+	"exportTask": "कार्य निर्यात करें",
+	"deleteTask": "कार्य हटाएं",
+	"deleteTaskMessage": "क्या आप वाकई इस कार्य को हटाना चाहते हैं? यह क्रिया पूर्ववत नहीं की जा सकती है।",
+	"cancel": "रद्द करें",
+	"delete": "हटाएं"
+}

+ 25 - 0
webview-ui/src/i18n/locales/hu/history.json

@@ -0,0 +1,25 @@
+{
+	"recentTasks": "Legutóbbi feladatok",
+	"viewAll": "Összes megtekintése",
+	"tokens": "Tokens: ↑{{in}} ↓{{out}}",
+	"cache": "Gyorsítótár: +{{writes}} → {{reads}}",
+	"apiCost": "API költség: ${{cost}}",
+	"history": "Előzmények",
+	"done": "Kész",
+	"searchPlaceholder": "Előzmények keresése...",
+	"newest": "Legújabb",
+	"oldest": "Legrégebbi",
+	"mostExpensive": "Legdrágább",
+	"mostTokens": "Legtöbb token",
+	"mostRelevant": "Legrelevánsabb",
+	"deleteTaskTitle": "Feladat törlése (Shift + kattintás a megerősítés kihagyásához)",
+	"tokensLabel": "Tokens:",
+	"cacheLabel": "Gyorsítótár:",
+	"apiCostLabel": "API költség:",
+	"copyPrompt": "Prompt másolása",
+	"exportTask": "Feladat exportálása",
+	"deleteTask": "Feladat törlése",
+	"deleteTaskMessage": "Biztosan törölni szeretné ezt a feladatot? Ez a művelet nem vonható vissza.",
+	"cancel": "Mégsem",
+	"delete": "Törlés"
+}

+ 25 - 0
webview-ui/src/i18n/locales/it/history.json

@@ -0,0 +1,25 @@
+{
+	"recentTasks": "Attività recenti",
+	"viewAll": "Vedi tutto",
+	"tokens": "Tokens: ↑{{in}} ↓{{out}}",
+	"cache": "Cache: +{{writes}} → {{reads}}",
+	"apiCost": "Costo API: ${{cost}}",
+	"history": "Cronologia",
+	"done": "Fatto",
+	"searchPlaceholder": "Ricerca nella cronologia...",
+	"newest": "Più recenti",
+	"oldest": "Più vecchie",
+	"mostExpensive": "Più costose",
+	"mostTokens": "Più token",
+	"mostRelevant": "Più rilevanti",
+	"deleteTaskTitle": "Elimina attività (Shift + Clic per saltare conferma)",
+	"tokensLabel": "Tokens:",
+	"cacheLabel": "Cache:",
+	"apiCostLabel": "Costo API:",
+	"copyPrompt": "Copia prompt",
+	"exportTask": "Esporta attività",
+	"deleteTask": "Elimina attività",
+	"deleteTaskMessage": "Sei sicuro di voler eliminare questa attività? Questa azione non può essere annullata.",
+	"cancel": "Annulla",
+	"delete": "Elimina"
+}

+ 25 - 0
webview-ui/src/i18n/locales/ja/history.json

@@ -0,0 +1,25 @@
+{
+	"recentTasks": "最近のタスク",
+	"viewAll": "すべて表示",
+	"tokens": "Tokens: ↑{{in}} ↓{{out}}",
+	"cache": "キャッシュ: +{{writes}} → {{reads}}",
+	"apiCost": "API コスト: ${{cost}}",
+	"history": "履歴",
+	"done": "完了",
+	"searchPlaceholder": "履歴をあいまい検索...",
+	"newest": "最新",
+	"oldest": "最古",
+	"mostExpensive": "最も高価",
+	"mostTokens": "最多トークン",
+	"mostRelevant": "最も関連性の高い",
+	"deleteTaskTitle": "タスクを削除(Shift + クリックで確認をスキップ)",
+	"tokensLabel": "Tokens:",
+	"cacheLabel": "キャッシュ:",
+	"apiCostLabel": "API コスト:",
+	"copyPrompt": "プロンプトをコピー",
+	"exportTask": "タスクをエクスポート",
+	"deleteTask": "タスクを削除",
+	"deleteTaskMessage": "このタスクを削除してもよろしいですか?この操作は元に戻せません。",
+	"cancel": "キャンセル",
+	"delete": "削除"
+}

+ 25 - 0
webview-ui/src/i18n/locales/ko/history.json

@@ -0,0 +1,25 @@
+{
+	"recentTasks": "최근 작업",
+	"viewAll": "모두 보기",
+	"tokens": "Tokens: ↑{{in}} ↓{{out}}",
+	"cache": "캐시: +{{writes}} → {{reads}}",
+	"apiCost": "API 비용: ${{cost}}",
+	"history": "기록",
+	"done": "완료",
+	"searchPlaceholder": "기록 검색...",
+	"newest": "최신순",
+	"oldest": "오래된순",
+	"mostExpensive": "가장 비싼순",
+	"mostTokens": "토큰 많은순",
+	"mostRelevant": "관련성 높은순",
+	"deleteTaskTitle": "작업 삭제 (Shift + 클릭으로 확인 생략)",
+	"tokensLabel": "Tokens:",
+	"cacheLabel": "캐시:",
+	"apiCostLabel": "API 비용:",
+	"copyPrompt": "프롬프트 복사",
+	"exportTask": "작업 내보내기",
+	"deleteTask": "작업 삭제",
+	"deleteTaskMessage": "이 작업을 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.",
+	"cancel": "취소",
+	"delete": "삭제"
+}

+ 25 - 0
webview-ui/src/i18n/locales/pl/history.json

@@ -0,0 +1,25 @@
+{
+	"recentTasks": "Ostatnie zadania",
+	"viewAll": "Zobacz wszystkie",
+	"tokens": "Tokens: ↑{{in}} ↓{{out}}",
+	"cache": "Pamięć podręczna: +{{writes}} → {{reads}}",
+	"apiCost": "Koszt API: ${{cost}}",
+	"history": "Historia",
+	"done": "Gotowe",
+	"searchPlaceholder": "Szukaj w historii...",
+	"newest": "Najnowsze",
+	"oldest": "Najstarsze",
+	"mostExpensive": "Najdroższe",
+	"mostTokens": "Najwięcej tokenów",
+	"mostRelevant": "Najbardziej trafne",
+	"deleteTaskTitle": "Usuń zadanie (Shift + Klik, aby pominąć potwierdzenie)",
+	"tokensLabel": "Tokens:",
+	"cacheLabel": "Pamięć podręczna:",
+	"apiCostLabel": "Koszt API:",
+	"copyPrompt": "Kopiuj prompt",
+	"exportTask": "Eksportuj zadanie",
+	"deleteTask": "Usuń zadanie",
+	"deleteTaskMessage": "Czy na pewno chcesz usunąć to zadanie? Tej akcji nie można cofnąć.",
+	"cancel": "Anuluj",
+	"delete": "Usuń"
+}

+ 25 - 0
webview-ui/src/i18n/locales/pt-BR/history.json

@@ -0,0 +1,25 @@
+{
+	"recentTasks": "Tarefas recentes",
+	"viewAll": "Ver todas",
+	"tokens": "Tokens: ↑{{in}} ↓{{out}}",
+	"cache": "Cache: +{{writes}} → {{reads}}",
+	"apiCost": "Custo da API: ${{cost}}",
+	"history": "Histórico",
+	"done": "Concluído",
+	"searchPlaceholder": "Pesquisar no histórico...",
+	"newest": "Mais recentes",
+	"oldest": "Mais antigas",
+	"mostExpensive": "Mais caras",
+	"mostTokens": "Mais tokens",
+	"mostRelevant": "Mais relevantes",
+	"deleteTaskTitle": "Excluir tarefa (Shift + Clique para pular confirmação)",
+	"tokensLabel": "Tokens:",
+	"cacheLabel": "Cache:",
+	"apiCostLabel": "Custo da API:",
+	"copyPrompt": "Copiar prompt",
+	"exportTask": "Exportar tarefa",
+	"deleteTask": "Excluir tarefa",
+	"deleteTaskMessage": "Tem certeza que deseja excluir esta tarefa? Esta ação não pode ser desfeita.",
+	"cancel": "Cancelar",
+	"delete": "Excluir"
+}

+ 25 - 0
webview-ui/src/i18n/locales/pt/history.json

@@ -0,0 +1,25 @@
+{
+	"recentTasks": "Tarefas recentes",
+	"viewAll": "Ver todas",
+	"tokens": "Tokens: ↑{{in}} ↓{{out}}",
+	"cache": "Cache: +{{writes}} → {{reads}}",
+	"apiCost": "Custo da API: ${{cost}}",
+	"history": "Histórico",
+	"done": "Concluído",
+	"searchPlaceholder": "Pesquisar no histórico...",
+	"newest": "Mais recentes",
+	"oldest": "Mais antigas",
+	"mostExpensive": "Mais caras",
+	"mostTokens": "Mais tokens",
+	"mostRelevant": "Mais relevantes",
+	"deleteTaskTitle": "Eliminar tarefa (Shift + Clique para ignorar confirmação)",
+	"tokensLabel": "Tokens:",
+	"cacheLabel": "Cache:",
+	"apiCostLabel": "Custo da API:",
+	"copyPrompt": "Copiar prompt",
+	"exportTask": "Exportar tarefa",
+	"deleteTask": "Eliminar tarefa",
+	"deleteTaskMessage": "Tem a certeza que pretende eliminar esta tarefa? Esta ação não pode ser desfeita.",
+	"cancel": "Cancelar",
+	"delete": "Eliminar"
+}

+ 25 - 0
webview-ui/src/i18n/locales/ru/history.json

@@ -0,0 +1,25 @@
+{
+	"recentTasks": "Недавние задачи",
+	"viewAll": "Показать все",
+	"tokens": "Tokens: ↑{{in}} ↓{{out}}",
+	"cache": "Кэш: +{{writes}} → {{reads}}",
+	"apiCost": "Стоимость API: ${{cost}}",
+	"history": "История",
+	"done": "Готово",
+	"searchPlaceholder": "Поиск по истории...",
+	"newest": "Новейшие",
+	"oldest": "Старейшие",
+	"mostExpensive": "Самые дорогие",
+	"mostTokens": "Больше всего токенов",
+	"mostRelevant": "Наиболее релевантные",
+	"deleteTaskTitle": "Удалить задачу (Shift + Клик для пропуска подтверждения)",
+	"tokensLabel": "Tokens:",
+	"cacheLabel": "Кэш:",
+	"apiCostLabel": "Стоимость API:",
+	"copyPrompt": "Копировать запрос",
+	"exportTask": "Экспортировать задачу",
+	"deleteTask": "Удалить задачу",
+	"deleteTaskMessage": "Вы уверены, что хотите удалить эту задачу? Это действие нельзя отменить.",
+	"cancel": "Отмена",
+	"delete": "Удалить"
+}

+ 25 - 0
webview-ui/src/i18n/locales/tr/history.json

@@ -0,0 +1,25 @@
+{
+	"recentTasks": "Son Görevler",
+	"viewAll": "Tümünü Gör",
+	"tokens": "Tokens: ↑{{in}} ↓{{out}}",
+	"cache": "Önbellek: +{{writes}} → {{reads}}",
+	"apiCost": "API Maliyeti: ${{cost}}",
+	"history": "Geçmiş",
+	"done": "Tamam",
+	"searchPlaceholder": "Geçmişte ara...",
+	"newest": "En Yeni",
+	"oldest": "En Eski",
+	"mostExpensive": "En Pahalı",
+	"mostTokens": "En Çok Token",
+	"mostRelevant": "En İlgili",
+	"deleteTaskTitle": "Görevi Sil (Onayı atlamak için Shift + Tıkla)",
+	"tokensLabel": "Tokens:",
+	"cacheLabel": "Önbellek:",
+	"apiCostLabel": "API Maliyeti:",
+	"copyPrompt": "Promptu Kopyala",
+	"exportTask": "Görevi Dışa Aktar",
+	"deleteTask": "Görevi Sil",
+	"deleteTaskMessage": "Bu görevi silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.",
+	"cancel": "İptal",
+	"delete": "Sil"
+}

+ 25 - 0
webview-ui/src/i18n/locales/zh-CN/history.json

@@ -0,0 +1,25 @@
+{
+	"recentTasks": "最近任务",
+	"viewAll": "查看全部",
+	"tokens": "Tokens: ↑{{in}} ↓{{out}}",
+	"cache": "缓存: +{{writes}} → {{reads}}",
+	"apiCost": "API 成本: ${{cost}}",
+	"history": "历史记录",
+	"done": "完成",
+	"searchPlaceholder": "模糊搜索历史...",
+	"newest": "最新",
+	"oldest": "最早",
+	"mostExpensive": "最贵",
+	"mostTokens": "最多令牌",
+	"mostRelevant": "最相关",
+	"deleteTaskTitle": "删除任务(Shift + 点击跳过确认)",
+	"tokensLabel": "Tokens:",
+	"cacheLabel": "缓存:",
+	"apiCostLabel": "API 成本:",
+	"copyPrompt": "复制提示词",
+	"exportTask": "导出任务",
+	"deleteTask": "删除任务",
+	"deleteTaskMessage": "确定要删除此任务吗?此操作无法撤销。",
+	"cancel": "取消",
+	"delete": "删除"
+}

+ 25 - 0
webview-ui/src/i18n/locales/zh-TW/history.json

@@ -0,0 +1,25 @@
+{
+	"recentTasks": "最近任務",
+	"viewAll": "查看全部",
+	"tokens": "Tokens: ↑{{in}} ↓{{out}}",
+	"cache": "快取: +{{writes}} → {{reads}}",
+	"apiCost": "API 費用: ${{cost}}",
+	"history": "歷史記錄",
+	"done": "完成",
+	"searchPlaceholder": "模糊搜尋歷史...",
+	"newest": "最新",
+	"oldest": "最舊",
+	"mostExpensive": "最昂貴",
+	"mostTokens": "最多令牌",
+	"mostRelevant": "最相關",
+	"deleteTaskTitle": "刪除任務(Shift + 點擊跳過確認)",
+	"tokensLabel": "Tokens:",
+	"cacheLabel": "快取:",
+	"apiCostLabel": "API 費用:",
+	"copyPrompt": "複製提示詞",
+	"exportTask": "匯出任務",
+	"deleteTask": "刪除任務",
+	"deleteTaskMessage": "確定要刪除此任務嗎?此操作無法復原。",
+	"cancel": "取消",
+	"delete": "刪除"
+}