From a13ff383c39ddf28cd8c3e37530c9c69b632c529 Mon Sep 17 00:00:00 2001 From: QiQiLu Date: Tue, 18 Aug 2026 21:22:47 +0800 Subject: [PATCH 1/3] fix(windows): complete Simplified Chinese localization --- .../components/themed-file-icon.tsx | 6 +- .../ui/components/extension-dialog.tsx | 6 +- .../ui/components/extensions-sidebar.tsx | 294 +- .../ui/components/external-extension-view.tsx | 6 +- .../src/extensions/ui/components/pro-gate.tsx | 6 +- .../components/v0-design-system-command.tsx | 80 +- .../extensions/viewers/csv/csv-preview.tsx | 24 +- .../components/agent-session-sidebar-item.tsx | 14 +- .../components/chat/acp-permission-prompt.tsx | 28 +- .../ai/components/chat/agent-shortcuts.tsx | 33 +- .../features/ai/components/chat/ai-chat.tsx | 6 +- .../ai/components/chat/chat-header.tsx | 37 +- .../ai/components/chat/chat-message.tsx | 30 +- .../history/chat-history-dropdown.tsx | 20 +- .../ai/components/input/chat-input-bar.tsx | 30 +- .../input/chat-preferences-menu.tsx | 43 +- .../components/mentions/ai-file-selector.tsx | 20 +- .../mentions/slash-command-dropdown.tsx | 10 +- .../components/messages/markdown-renderer.tsx | 29 +- .../components/messages/tool-call-display.tsx | 54 +- .../components/provider-api-key-command.tsx | 28 +- .../components/selectors/context-selector.tsx | 35 +- .../components/selectors/model-selector.tsx | 8 +- .../selectors/provider-selector.tsx | 6 +- .../ai/components/skills/skills-command.tsx | 71 +- .../features/ai/hooks/use-agent-options.ts | 10 +- .../features/ai/hooks/use-ai-model-options.ts | 16 +- .../src/features/ai/hooks/use-voice-input.ts | 24 +- .../ai/integrations/codex/codex-settings.tsx | 77 +- .../components/collaboration-avatar.tsx | 5 +- .../collaboration-channel-icons.tsx | 31 +- .../components/collaboration-media-footer.tsx | 19 +- .../collaboration-message-composer.tsx | 8 +- .../components/collaboration-sidebar.tsx | 173 +- .../collaboration/components/emoji-picker.tsx | 95 +- .../components/command-palette.tsx | 12 +- .../components/icon-theme-selector.tsx | 10 +- .../components/theme-selector.tsx | 20 +- .../command-palette/constants/git-actions.tsx | 69 +- .../constants/view-actions.tsx | 6 +- .../utils/action-localization.ts | 38 + .../database/components/cell-renderer.tsx | 6 +- .../database/components/column-filters.tsx | 46 +- .../connection/connection-dialog.tsx | 87 +- .../connection/connection-validation.ts | 31 +- .../database/components/context-menus.tsx | 17 +- .../database/components/crud-modals.tsx | 51 +- .../database/components/data-grid.tsx | 24 +- .../database/components/database-sidebar.tsx | 95 +- .../database/components/info-view.tsx | 20 +- .../database/components/pagination.tsx | 18 +- .../database/components/query-bar.tsx | 26 +- .../database/components/sql-history-list.tsx | 25 +- .../database/components/table-sidebar.tsx | 31 +- .../database/components/table-toolbar.tsx | 67 +- .../features/database/lib/database-catalog.ts | 12 +- .../database/lib/query-result-summary.ts | 13 +- .../providers/mongodb/mongodb-viewer.tsx | 60 +- .../components/create-subscription-dialog.tsx | 28 +- .../database/providers/redis/redis-viewer.tsx | 38 +- .../providers/sql/sql-database-viewer.tsx | 10 +- .../debugger/components/debugger-panels.tsx | 29 +- .../components/debugger-variables-panel.tsx | 12 +- .../debugger/components/debugger-view.tsx | 73 +- .../components/debugger-watch-panel.tsx | 16 +- .../docker/components/docker-sidebar.tsx | 514 +- .../editor/components/code-editor.tsx | 32 +- .../editor/components/html/html-preview.tsx | 6 +- .../toolbar/editor-status-actions.tsx | 21 +- .../toolbar/file-path-breadcrumb.tsx | 11 +- .../components/toolbar/path-breadcrumb.tsx | 4 +- .../components/toolbar/symbol-breadcrumb.tsx | 4 +- .../inline-edit-model-selector.tsx | 24 +- .../inline-edit/inline-edit-popover.tsx | 21 +- .../editor/inline-edit/use-inline-edit.ts | 27 +- .../src/features/editor/lsp/rename-input.tsx | 4 +- .../markdown/markdown-document-view.tsx | 4 +- .../src/features/editor/markdown/parser.ts | 10 +- .../markdown/use-highlighted-markdown.ts | 6 +- .../editor/notebook/notebook-editor.tsx | 18 +- .../features/editor/stores/buffer.store.ts | 10 +- .../components/file-explorer-pane.tsx | 4 +- .../components/file-explorer-tree-item.tsx | 16 +- .../components/file-explorer-tree.tsx | 111 +- .../components/file-navigator-sidebar.tsx | 27 +- .../hooks/use-file-explorer-context-menu.tsx | 4 +- .../file-explorer/lib/env-template.ts | 10 +- .../components/ide-settings-import-dialog.tsx | 42 +- .../components/linux-folder-picker-dialog.tsx | 30 +- .../components/project-name-menu.tsx | 9 +- .../workspace-project-transition.ts | 41 +- .../file-system/stores/file-system.store.ts | 98 +- .../stores/recent-folders.store.ts | 9 +- .../extension-generation-command.tsx | 158 +- .../git/components/diff/git-diff-binary.tsx | 9 +- .../components/diff/git-diff-editor-stack.tsx | 59 +- .../git/components/diff/git-diff-header.tsx | 24 +- .../git/components/diff/git-diff-image.tsx | 43 +- .../git/components/diff/git-diff-text.tsx | 6 +- .../git/components/diff/git-diff-viewer.tsx | 6 +- .../git/components/git-actions-menu.tsx | 30 +- .../git/components/git-commit-history.tsx | 4 +- .../git/components/git-commit-panel.tsx | 28 +- .../git/components/git-operation-banner.tsx | 55 +- .../git/components/git-project-selector.tsx | 28 +- .../components/git-pull-strategy-dialog.tsx | 21 +- .../git/components/git-remote-manager.tsx | 24 +- .../git/components/git-tag-manager.tsx | 125 +- .../src/features/git/components/git-view.tsx | 76 +- .../git/components/stash/git-stash-modal.tsx | 10 +- .../status/git-status-file-item.tsx | 8 +- .../components/status/git-status-panel.tsx | 40 +- .../git/hooks/use-git-diff-actions.ts | 84 +- .../features/git/hooks/use-git-diff-data.ts | 5 +- .../git/hooks/use-git-log-controller.ts | 8 +- .../git/services/working-tree-diff-loader.ts | 4 +- .../src/features/git/types/git-diff.types.ts | 2 + .../features/git/utils/diff-buffer-label.ts | 12 +- .../github/components/comment-item.tsx | 28 +- .../github/components/commit-item.tsx | 20 +- .../github/components/file-diff-view.tsx | 16 +- .../components/github-action-viewer.tsx | 144 +- .../github/components/github-actions-view.tsx | 48 +- .../github/components/github-auth-status.tsx | 42 +- .../github/components/github-create-view.tsx | 90 +- .../components/github-inline-editors.tsx | 19 +- .../github/components/github-issue-viewer.tsx | 130 +- .../github/components/github-issues-view.tsx | 23 +- .../github-markdown-editor-controls.tsx | 19 +- .../components/github-markdown-editor.tsx | 6 +- .../github/components/github-markdown.tsx | 6 +- .../components/github-metadata-pickers.tsx | 27 +- .../components/github-notifications-menu.tsx | 21 +- .../components/github-pr-inline-action.tsx | 34 +- .../github/components/github-pr-sidebar.tsx | 28 +- .../components/github-pr-viewer-header.tsx | 30 +- .../github/components/github-pr-viewer.tsx | 79 +- .../github/components/github-prs-view.tsx | 114 +- .../github/components/pr-activity-panel.tsx | 22 +- .../github/components/pr-files-panel.tsx | 39 +- .../features/github/components/pr-status.tsx | 37 +- .../github/utils/github-markdown-content.ts | 15 +- .../github/utils/github-viewer-utils.ts | 7 +- .../components/global-search-buffer.tsx | 6 +- .../components/global-search-results.tsx | 5 +- .../commands/editor-command-actions.ts | 55 +- .../keymaps/commands/lsp-command-actions.ts | 19 +- .../commands/navigation-command-actions.ts | 32 +- .../keymaps/components/keybinding-input.tsx | 8 +- .../keymaps/components/keybinding-row.tsx | 38 +- .../keymaps/utils/command-localization.ts | 49 + .../layout/components/app-update-control.tsx | 32 +- .../components/bottom-pane/bottom-pane.tsx | 4 +- .../footer/footer-debugger-item.tsx | 25 +- .../footer/footer-file-path-item.tsx | 4 +- .../footer/footer-git-branch-item.tsx | 4 +- .../layout/components/footer/footer.tsx | 2 +- .../layout/components/main-layout.tsx | 6 +- .../components/plugin-activity-rail.tsx | 9 +- .../layout/components/resizable-pane.tsx | 4 +- .../components/sidebar/main-sidebar.tsx | 12 +- .../components/sidebar/sidebar-history.tsx | 76 +- .../sidebar/sidebar-pane-selector.tsx | 4 +- .../components/sidebar/sidebar-projects.tsx | 24 +- .../components/local-history-command.tsx | 68 +- .../local-history/utils/open-local-history.ts | 9 +- .../onboarding/components/onboarding-view.tsx | 36 +- .../components/release-notes-content.tsx | 4 +- .../onboarding/lib/onboarding-view-model.ts | 28 +- .../outline/components/outline-command.tsx | 16 +- .../outline/components/outline-sidebar.tsx | 44 +- .../panes/components/pane-container.tsx | 74 +- .../panes/components/pane-resize-handle.tsx | 4 +- .../features/panes/constants/tool-buffers.ts | 11 +- .../references/components/references-pane.tsx | 16 +- .../remote/components/connection-form.tsx | 30 +- .../components/password-prompt-dialog.tsx | 41 +- .../services/remote-connection-actions.ts | 7 +- .../components/run-action-dialog.tsx | 24 +- .../components/run-actions-button.tsx | 52 +- .../src/features/run/components/run-pane.tsx | 5 +- .../custom-theme-creator-dialog.tsx | 45 +- .../settings/components/font-selector.tsx | 15 +- .../components/macos-settings-panels.tsx | 20 +- .../components/settings-vertical-tabs.tsx | 6 +- .../components/tabs/account-settings.tsx | 86 +- .../settings/components/tabs/ai-settings.tsx | 193 +- .../tabs/collaboration-settings.tsx | 100 +- .../components/tabs/enterprise-settings.tsx | 65 +- .../components/tabs/keyboard-settings.tsx | 7 +- .../features/tabs/components/tab-bar-item.tsx | 18 +- .../src/features/tabs/components/tab-bar.tsx | 62 +- .../components/terminal-error-boundary.tsx | 10 +- .../terminal/components/terminal-search.tsx | 10 +- .../components/terminal-tab-bar-item.tsx | 14 +- .../terminal/components/terminal-tab-bar.tsx | 70 +- .../components/terminal-tab-context-menu.tsx | 20 +- .../features/terminal/components/terminal.tsx | 8 +- .../terminal/hooks/use-terminal-addons.ts | 12 +- .../binary/components/binary-file-viewer.tsx | 34 +- .../components/viewer-zoom-controls.tsx | 9 +- .../image/components/image-context-menu.tsx | 30 +- .../viewer/image/components/image-viewer.tsx | 16 +- .../components/image-editor-toolbar.tsx | 26 +- .../editor/components/image-format-dialog.tsx | 62 +- .../editor/components/image-resize-dialog.tsx | 16 +- .../editor/hooks/use-image-operations.ts | 34 +- .../viewer/pdf/components/pdf-viewer.tsx | 39 +- .../web/components/web-viewer-toolbar.tsx | 20 +- .../viewer/web/components/web-viewer.tsx | 10 +- .../viewer/web/hooks/use-embedded-webview.ts | 6 +- .../viewer/web/utils/web-viewer-url.ts | 14 +- .../window/components/account-menu.tsx | 48 +- .../window/components/new-project-content.tsx | 44 +- .../window/components/project-icon-picker.tsx | 14 +- .../window/components/project-picker.tsx | 56 +- .../window/components/title-bar/title-bar.tsx | 14 +- .../components/title-bar/window-controls.tsx | 14 +- .../components/unsaved-changes-dialog.tsx | 14 +- .../window/components/window-menu-bar.tsx | 202 +- .../window/components/zoom-indicator.tsx | 4 +- .../features/window/hooks/use-deep-link.ts | 18 +- .../window/hooks/use-desktop-sign-in.ts | 12 +- .../window/hooks/use-menu-events-wrapper.ts | 14 +- .../features/window/lib/new-project-model.ts | 16 +- .../src/features/window/stores/auth.store.ts | 12 +- .../features/window/stores/project.store.ts | 15 +- .../window/utils/window-open-request.ts | 22 +- windows/tauri/src/i18n/locale.ts | 5836 ++++++++++++++++- windows/tauri/src/ui/breadcrumb.tsx | 9 +- windows/tauri/src/ui/carousel.tsx | 13 +- windows/tauri/src/ui/command.tsx | 16 +- windows/tauri/src/ui/dialog.tsx | 79 +- windows/tauri/src/ui/dropdown.tsx | 4 +- windows/tauri/src/ui/number-input.tsx | 6 +- windows/tauri/src/ui/pagination.tsx | 27 +- windows/tauri/src/ui/search.tsx | 38 +- windows/tauri/src/ui/select.tsx | 23 +- windows/tauri/src/ui/sheet.tsx | 5 +- windows/tauri/src/ui/sidebar.tsx | 13 +- windows/tauri/src/ui/sonner.tsx | 4 +- windows/tauri/src/ui/spinner.tsx | 11 +- windows/tauri/src/ui/toast.tsx | 5 +- 243 files changed, 10678 insertions(+), 3296 deletions(-) create mode 100644 windows/tauri/src/features/command-palette/utils/action-localization.ts create mode 100644 windows/tauri/src/features/keymaps/utils/command-localization.ts diff --git a/windows/tauri/src/extensions/icon-themes/components/themed-file-icon.tsx b/windows/tauri/src/extensions/icon-themes/components/themed-file-icon.tsx index 669f6fe35..f5898e1f2 100644 --- a/windows/tauri/src/extensions/icon-themes/components/themed-file-icon.tsx +++ b/windows/tauri/src/extensions/icon-themes/components/themed-file-icon.tsx @@ -2,6 +2,7 @@ import DOMPurify from "dompurify"; import { cloneElement, isValidElement, useMemo, useSyncExternalStore } from "react"; import { themeRegistry } from "@/extensions/themes/theme-registry"; import { getDefaultSetting, useSettingsStore } from "@/features/settings/stores/settings.store"; +import { useTranslation } from "@/i18n/locale-provider"; import { cn } from "@/utils/cn"; import { iconThemeRegistry } from "../icon-theme-registry"; @@ -27,6 +28,7 @@ export function ThemedFileIcon({ isSymlink = false, className = "text-subtle-foreground", }: ThemedFileIconProps) { + const { t } = useTranslation(); const iconThemeId = useSettingsStore((state) => state.settings.iconTheme); useSyncExternalStore( (callback) => iconThemeRegistry.onRegistryChange(callback), @@ -95,9 +97,9 @@ export function ThemedFileIcon({ viewBox="0 0 16 16" className="-bottom-0.5 -right-0.5 themed-file-icon-badge absolute text-primary" role="img" - aria-label="Symlink" + aria-label={t("ui.symlink")} > - Symlink + {t("ui.symlink")} {dialog.title} } + render={ + ) : isUnavailableAgent ? ( - ) : extension.isInstalled ? ( @@ -547,6 +536,7 @@ const ExtensionRow = ({ }; export const ExtensionsSidebar = () => { + const { t } = useTranslation(); const settings = useSettingsStore( useShallow((state) => ({ aiSkills: state.settings.aiSkills, @@ -614,7 +604,9 @@ export const ExtensionsSidebar = () => { id: `agent:${contribution.id}`, name: agent?.name ?? contribution.name, description: - agent?.description ?? contribution.description ?? "ACP-compatible coding agent", + agent?.description ?? + contribution.description ?? + t("extensions.agentFallbackDescription"), category: "agent", isInstalled: agent?.installed ?? false, isEnabled: agent?.installed ?? false, @@ -837,7 +829,8 @@ export const ExtensionsSidebar = () => { allExtensions.push({ id: theme.id, name: theme.name, - description: theme.description || `${theme.category} theme`, + description: + theme.description || t("extensions.themeFallbackDescription", { category: theme.category }), category: "theme", isInstalled: true, isEnabled: true, @@ -863,7 +856,9 @@ export const ExtensionsSidebar = () => { allExtensions.push({ id: iconTheme.id, name: iconTheme.name, - description: iconTheme.description || `${iconTheme.name} icon theme`, + description: + iconTheme.description || + t("extensions.iconThemeFallbackDescription", { name: iconTheme.name }), category: "icon-theme", isInstalled: true, isEnabled: true, @@ -893,12 +888,15 @@ export const ExtensionsSidebar = () => { allExtensions.push({ id: skill.id, name: skill.title, - description: skill.description || preview || "Reusable AI chat instructions", + description: skill.description || preview || t("extensions.reusableSkillDescription"), category: "skill", isInstalled: true, isEnabled: true, - version: skill.version || (skill.source === "marketplace" ? undefined : "Local"), - publisher: skill.author || (skill.source === "marketplace" ? "Marketplace" : "You"), + version: + skill.version || (skill.source === "marketplace" ? undefined : t("extensions.local")), + publisher: + skill.author || + (skill.source === "marketplace" ? t("extensions.marketplace") : t("extensions.you")), isMarketplace: skill.source === "marketplace", icon: getCatalogIconUrl(skill.title, skill.author, "codex"), skill, @@ -941,12 +939,12 @@ export const ExtensionsSidebar = () => { allExtensions.push({ id: `agent:${agent.id}`, name: agent.name, - description: agent.description ?? "ACP-compatible coding agent", + description: agent.description ?? t("extensions.agentFallbackDescription"), category: "agent", isInstalled: agent.installed, isEnabled: agent.installed, extensions: [agent.binaryName], - publisher: "Marketplace", + publisher: t("extensions.marketplace"), isMarketplace: true, agentId: agent.id, icon: resolveManifestIcon(agent.icon ?? undefined, agent.id, agent.name, agent.binaryName), @@ -963,6 +961,7 @@ export const ExtensionsSidebar = () => { settings.aiSkills, settings.iconTheme, settings.theme, + t, ]); useEffect(() => { @@ -997,15 +996,18 @@ export const ExtensionsSidebar = () => { ); showToast({ message: updatedSkill.localOverride - ? `${extension.name} updated, local override kept` - : `${extension.name} updated successfully`, + ? t("extensions.updatedWithLocalOverride", { name: extension.name }) + : t("extensions.updatedSuccessfully", { name: extension.name }), type: "success", duration: 3000, }); } catch (error) { console.error(`Failed to update ${extension.name}:`, error); showToast({ - message: `Failed to update ${extension.name}: ${getErrorMessage(error)}`, + message: t("extensions.updateFailed", { + name: extension.name, + message: getErrorMessage(error, t("extensions.unknownError")), + }), type: "error", duration: 5000, }); @@ -1016,14 +1018,17 @@ export const ExtensionsSidebar = () => { try { await updateExtension(extension.id); showToast({ - message: `${extension.name} updated successfully`, + message: t("extensions.updatedSuccessfully", { name: extension.name }), type: "success", duration: 3000, }); } catch (error) { console.error(`Failed to update ${extension.name}:`, error); showToast({ - message: `Failed to update ${extension.name}: ${getErrorMessage(error)}`, + message: t("extensions.updateFailed", { + name: extension.name, + message: getErrorMessage(error, t("extensions.unknownError")), + }), type: "error", duration: 5000, }); @@ -1041,14 +1046,17 @@ export const ExtensionsSidebar = () => { ), ); showToast({ - message: `${extension.name} reset to marketplace version`, + message: t("extensions.resetToMarketplaceSuccess", { name: extension.name }), type: "success", duration: 3000, }); } catch (error) { console.error(`Failed to reset ${extension.name}:`, error); showToast({ - message: `Failed to reset ${extension.name}: ${getErrorMessage(error)}`, + message: t("extensions.resetFailed", { + name: extension.name, + message: getErrorMessage(error, t("extensions.unknownError")), + }), type: "error", duration: 5000, }); @@ -1069,14 +1077,19 @@ export const ExtensionsSidebar = () => { } await updateSetting(settingKey, nextSelectionId); showToast({ - message: `${getAppearanceOptionLabel(extension, nextSelectionId)} selected`, + message: t("extensions.selected", { + name: getAppearanceOptionLabel(extension, nextSelectionId), + }), type: "success", duration: 2500, }); } catch (error) { console.error(`Failed to use ${extension.name}:`, error); showToast({ - message: `Failed to use ${extension.name}: ${getErrorMessage(error)}`, + message: t("extensions.useFailed", { + name: extension.name, + message: getErrorMessage(error, t("extensions.unknownError")), + }), type: "error", duration: 5000, }); @@ -1092,14 +1105,17 @@ export const ExtensionsSidebar = () => { try { await enableExtension(extension.id); showToast({ - message: `${extension.name} activated`, + message: t("extensions.activated", { name: extension.name }), type: "success", duration: 2500, }); } catch (error) { console.error(`Failed to activate ${extension.name}:`, error); showToast({ - message: `Failed to activate ${extension.name}: ${getErrorMessage(error)}`, + message: t("extensions.activateFailed", { + name: extension.name, + message: getErrorMessage(error, t("extensions.unknownError")), + }), type: "error", duration: 5000, }); @@ -1115,14 +1131,17 @@ export const ExtensionsSidebar = () => { try { await disableExtension(extension.id); showToast({ - message: `${extension.name} deactivated`, + message: t("extensions.deactivated", { name: extension.name }), type: "success", duration: 2500, }); } catch (error) { console.error(`Failed to deactivate ${extension.name}:`, error); showToast({ - message: `Failed to deactivate ${extension.name}: ${getErrorMessage(error)}`, + message: t("extensions.deactivateFailed", { + name: extension.name, + message: getErrorMessage(error, t("extensions.unknownError")), + }), type: "error", duration: 5000, }); @@ -1134,7 +1153,7 @@ export const ExtensionsSidebar = () => { if (extension.category === "agent") { if (!extension.isInstalled && extension.canInstall === false) { showToast({ - message: `${extension.name} cannot be installed automatically`, + message: t("extensions.cannotInstallAutomatically", { name: extension.name }), type: "error", duration: 5000, }); @@ -1159,11 +1178,11 @@ export const ExtensionsSidebar = () => { showToast({ message: extension.isInstalled ? managedUninstallLeftGlobalBinary - ? `${extension.name} managed install removed` - : `${extension.name} uninstalled successfully` - : `${extension.name} installed successfully`, + ? t("extensions.managedInstallRemoved", { name: extension.name }) + : t("extensions.uninstalledSuccessfully", { name: extension.name }) + : t("extensions.installedSuccessfully", { name: extension.name }), description: managedUninstallLeftGlobalBinary - ? "A global installation is still detected on your PATH." + ? t("extensions.globalInstallStillDetected") : undefined, type: managedUninstallLeftGlobalBinary ? "info" : "success", duration: managedUninstallLeftGlobalBinary ? 5000 : 3000, @@ -1174,9 +1193,14 @@ export const ExtensionsSidebar = () => { error, ); showToast({ - message: `Failed to ${extension.isInstalled ? "uninstall" : "install"} ${extension.name}: ${getErrorMessage( - error, - )}`, + message: extension.isInstalled + ? t("extensions.uninstallFailed", { + name: extension.name, + message: getErrorMessage(error, t("extensions.unknownError")), + }) + : t("extensions.installFailed", { + message: `${extension.name}: ${getErrorMessage(error, t("extensions.unknownError"))}`, + }), type: "error", duration: 5000, }); @@ -1201,7 +1225,7 @@ export const ExtensionsSidebar = () => { ), ); showToast({ - message: `${extension.name} removed successfully`, + message: t("extensions.removedSuccessfully", { name: extension.name }), type: "success", duration: 3000, }); @@ -1217,14 +1241,17 @@ export const ExtensionsSidebar = () => { ...settings.aiSkills, ]); showToast({ - message: `${extension.name} added successfully`, + message: t("extensions.addedSuccessfully", { name: extension.name }), type: "success", duration: 3000, }); } catch (error) { console.error(`Failed to update ${extension.name}:`, error); showToast({ - message: `Failed to update ${extension.name}: ${getErrorMessage(error)}`, + message: t("extensions.updateActionFailed", { + name: extension.name, + message: getErrorMessage(error, t("extensions.unknownError")), + }), type: "error", duration: 5000, }); @@ -1254,7 +1281,9 @@ export const ExtensionsSidebar = () => { await enableExtension(extension.id); } showToast({ - message: `${extension.name} ${extension.isEnabled ? "deactivated" : "activated"}`, + message: extension.isEnabled + ? t("extensions.deactivated", { name: extension.name }) + : t("extensions.activated", { name: extension.name }), type: "success", duration: 2500, }); @@ -1264,7 +1293,15 @@ export const ExtensionsSidebar = () => { error, ); showToast({ - message: `Failed to ${extension.isEnabled ? "deactivate" : "activate"} ${extension.name}: ${getErrorMessage(error)}`, + message: extension.isEnabled + ? t("extensions.deactivateFailed", { + name: extension.name, + message: getErrorMessage(error, t("extensions.unknownError")), + }) + : t("extensions.activateFailed", { + name: extension.name, + message: getErrorMessage(error, t("extensions.unknownError")), + }), type: "error", duration: 5000, }); @@ -1277,14 +1314,16 @@ export const ExtensionsSidebar = () => { try { await installExtension(extension.id); showToast({ - message: `${extension.name} installed successfully`, + message: t("extensions.installedSuccessfully", { name: extension.name }), type: "success", duration: 3000, }); } catch (error) { console.error(`Failed to install ${extension.name}:`, error); showToast({ - message: `Failed to install ${extension.name}: ${getErrorMessage(error)}`, + message: t("extensions.installFailed", { + message: `${extension.name}: ${getErrorMessage(error, t("extensions.unknownError"))}`, + }), type: "error", duration: 5000, }); @@ -1308,14 +1347,17 @@ export const ExtensionsSidebar = () => { try { await uninstallExtension(extension.id); showToast({ - message: `${extension.name} uninstalled successfully`, + message: t("extensions.uninstalledSuccessfully", { name: extension.name }), type: "success", duration: 3000, }); } catch (error) { console.error(`Failed to uninstall ${extension.name}:`, error); showToast({ - message: `Failed to uninstall ${extension.name}: ${getErrorMessage(error)}`, + message: t("extensions.uninstallFailed", { + name: extension.name, + message: getErrorMessage(error, t("extensions.unknownError")), + }), type: "error", duration: 5000, }); @@ -1404,12 +1446,12 @@ export const ExtensionsSidebar = () => { const isUnavailableAgent = extension.category === "agent" && !extension.isInstalled && extension.canInstall === false; const isAppearance = isAppearanceExtension(extension); - const primaryActionLabel = getPrimaryActionLabel(extension); + const primaryActionLabel = getPrimaryActionLabel(extension, t); if (extension.isBundled) { items.push({ id: "built-in", - label: "Built-in", + label: t("extensions.builtIn"), icon: , disabled: true, onClick: () => {}, @@ -1422,7 +1464,7 @@ export const ExtensionsSidebar = () => { if (!extension.isEnabled) { items.push({ id: "activate", - label: "Activate", + label: t("extensions.activate"), icon: , disabled: isInstalling, onClick: () => { @@ -1432,7 +1474,7 @@ export const ExtensionsSidebar = () => { } else { items.push({ id: "deactivate", - label: "Deactivate", + label: t("extensions.deactivate"), icon: , disabled: isInstalling, onClick: () => { @@ -1458,7 +1500,9 @@ export const ExtensionsSidebar = () => { const isCurrent = currentSelection === option.id; items.push({ id: `use-${option.id}`, - label: isCurrent ? `Current: ${option.name}` : `Use ${option.name}`, + label: isCurrent + ? t("extensions.currentName", { name: option.name }) + : t("extensions.useName", { name: option.name }), icon: ( ), @@ -1471,7 +1515,7 @@ export const ExtensionsSidebar = () => { } else if (extension.isEnabled) { items.push({ id: extension.isActive ? "active" : "use", - label: extension.isActive ? "Current" : "Use", + label: extension.isActive ? t("extensions.current") : t("extensions.use"), icon: , disabled: extension.isActive || isInstalling, onClick: () => { @@ -1482,7 +1526,7 @@ export const ExtensionsSidebar = () => { } else { items.push({ id: extension.isEnabled ? "deactivate" : "activate", - label: extension.isEnabled ? "Deactivate" : "Activate", + label: extension.isEnabled ? t("extensions.deactivate") : t("extensions.activate"), icon: extension.isEnabled ? ( ) : ( @@ -1499,7 +1543,7 @@ export const ExtensionsSidebar = () => { if ((hasUpdate || hasRuntimeIssue) && extension.isInstalled) { items.push({ id: "update", - label: hasRuntimeIssue ? "Reinstall" : "Update", + label: hasRuntimeIssue ? t("extensions.reinstall") : t("extensions.update"), icon: , disabled: isInstalling, onClick: () => { @@ -1511,7 +1555,7 @@ export const ExtensionsSidebar = () => { if (hasLocalOverride) { items.push({ id: "reset", - label: "Reset to Marketplace Version", + label: t("extensions.resetToMarketplaceVersion"), icon: , disabled: isInstalling, onClick: () => { @@ -1548,7 +1592,7 @@ export const ExtensionsSidebar = () => { } else if (extension.isMarketplace) { items.push({ id: "uninstall", - label: "Uninstall", + label: t("extensions.uninstall"), icon: , disabled: isInstalling, className: "text-destructive hover:text-destructive", @@ -1559,7 +1603,7 @@ export const ExtensionsSidebar = () => { } return items; - }, [extensionContextMenu.data, extensionsWithUpdates, installingAgentIds, availableExtensions]); + }, [availableExtensions, extensionContextMenu.data, extensionsWithUpdates, installingAgentIds, t]); return (
@@ -1568,17 +1612,19 @@ export const ExtensionsSidebar = () => {
-

Extensions

+

+ {t("extensions.title")} +

- {extensions.length} available + {t("extensions.availableCount", { count: extensions.length })} · - {installedCount} installed + {t("extensions.installedCount", { count: installedCount })} {updateCount > 0 ? ( <> · - {updateCount} update{updateCount === 1 ? "" : "s"} + {t("extensions.updatesCount", { count: updateCount })} ) : null} @@ -1592,7 +1638,7 @@ export const ExtensionsSidebar = () => { value={searchQuery} onChange={setSearchQuery} leftIcon={Search} - placeholder="Search extensions" + placeholder={t("extensions.searchPlaceholder")} size="md" containerClassName="min-w-0 flex-1 sm:w-80 sm:flex-none" className="h-9 bg-surface/45" @@ -1600,7 +1646,7 @@ export const ExtensionsSidebar = () => { {settings.extensionsActiveTab === "skill" ? ( ) : null}
@@ -1626,7 +1672,7 @@ export const ExtensionsSidebar = () => { onClick={() => void updateSetting("extensionsActiveTab", tab.id as ExtensionTabId)} > {Icon ? : null} - {tab.label} + {t(tab.labelKey)} { {settings.extensionsActiveTab === "skill" && isLoadingSkills ? (
- +
) : null} {settings.extensionsActiveTab === "agent" && isLoadingAgents ? (
- +
) : null} {filteredExtensions.length === 0 ? ( - + ) : (
{filteredExtensions.map((extension) => { @@ -1697,7 +1743,9 @@ export const ExtensionsSidebar = () => {
{selectedExtension.publisher ? ( - By {selectedExtension.publisher} + + {t("extensions.byPublisher", { publisher: selectedExtension.publisher })} + ) : null} {selectedExtension.version ? v{selectedExtension.version} : null}
@@ -1706,31 +1754,31 @@ export const ExtensionsSidebar = () => {
- {getCategoryLabel(selectedExtension.category)} + {getCategoryLabel(selectedExtension.category, t)} {selectedExtension.isInstalled ? ( - Installed + {t("extensions.installed")} ) : null} {selectedExtension.isInstalled && !selectedExtension.isEnabled ? ( - Disabled + {t("extensions.disabled")} ) : null} {hasExtensionUpdate(selectedExtension) ? ( - Update + {t("extensions.update")} ) : null} {selectedExtension.isActive ? ( - Active + {t("extensions.active")} ) : null} {selectedExtension.isBundled ? ( - Built-in + {t("extensions.builtIn")} ) : null}
@@ -1751,7 +1799,9 @@ export const ExtensionsSidebar = () => { selectedExtension.appearanceOptions?.length ? (
- {selectedExtension.category === "theme" ? "Themes" : "Icon themes"} + {selectedExtension.category === "theme" + ? t("extensions.themes") + : t("extensions.iconThemes")}
{selectedExtension.appearanceOptions.map((option) => { @@ -1785,10 +1835,10 @@ export const ExtensionsSidebar = () => { > {isCurrent - ? "Current" + ? t("extensions.current") : selectedExtension.isEnabled - ? "Use" - : "Activate and use"} + ? t("extensions.use") + : t("extensions.activateAndUse")}
); @@ -1842,7 +1892,7 @@ export const ExtensionsSidebar = () => { ) : ( )} - {getPrimaryActionLabel(selectedExtension)} + {getPrimaryActionLabel(selectedExtension, t)} ) : null} {selectedExtension.isMarketplace && @@ -1856,7 +1906,7 @@ export const ExtensionsSidebar = () => { disabled={isExtensionInstalling(selectedExtension)} > - Uninstall + {t("extensions.uninstall")} ) : null} {hasExtensionUpdate(selectedExtension) && selectedExtension.isInstalled ? ( @@ -1866,7 +1916,7 @@ export const ExtensionsSidebar = () => { disabled={isExtensionInstalling(selectedExtension)} > - Update + {t("extensions.update")} ) : null} {canDeactivateAppearanceExtension(selectedExtension) ? ( @@ -1876,7 +1926,7 @@ export const ExtensionsSidebar = () => { onClick={() => void handleDeactivateExtension(selectedExtension)} > - Deactivate + {t("extensions.deactivate")} ) : null} {selectedExtension.skill && hasSkillLocalOverride(selectedExtension.skill) ? ( @@ -1885,19 +1935,21 @@ export const ExtensionsSidebar = () => { onClick={() => void handleResetSkillOverride(selectedExtension)} > - Reset + {t("settings.keyboard.reset")} ) : null}
-
Contributions
+
+ {t("extensions.contributions")} +
{(selectedExtension.contributionSummary?.length ? selectedExtension.contributionSummary : selectedExtension.extensions ? selectedExtension.extensions - : [getCategoryLabel(selectedExtension.category)] + : [getCategoryLabel(selectedExtension.category, t)] ).map((item) => ( {item} @@ -1907,7 +1959,7 @@ export const ExtensionsSidebar = () => {
) : ( - + )}
diff --git a/windows/tauri/src/extensions/ui/components/external-extension-view.tsx b/windows/tauri/src/extensions/ui/components/external-extension-view.tsx index 48d20c3ad..d69189b0c 100644 --- a/windows/tauri/src/extensions/ui/components/external-extension-view.tsx +++ b/windows/tauri/src/extensions/ui/components/external-extension-view.tsx @@ -1,5 +1,6 @@ import { useCallback, useEffect, useState } from "react"; import { Empty, EmptyDescription, EmptyHeader, EmptyTitle } from "@/ui/empty"; +import { useTranslation } from "@/i18n/locale-provider"; import { Spinner } from "@/ui/spinner"; import { uiExtensionHost } from "../services/ui-extension-host"; import { useUIExtensionStore } from "../stores/ui-extension-store"; @@ -12,6 +13,7 @@ interface ExternalExtensionViewProps { } export function ExternalExtensionView({ extensionId, viewId }: ExternalExtensionViewProps) { + const { t } = useTranslation(); const revision = useUIExtensionStore((state) => state.viewRevisions.get(viewId) ?? 0); const [node, setNode] = useState(null); const [error, setError] = useState(null); @@ -43,14 +45,14 @@ export function ExternalExtensionView({ extensionId, viewId }: ExternalExtension return ( - Extension error + {t("extensions.extensionError")} {error} ); } if (!node) { - return ; + return ; } return ; } diff --git a/windows/tauri/src/extensions/ui/components/pro-gate.tsx b/windows/tauri/src/extensions/ui/components/pro-gate.tsx index 8f3b1677a..61c2d4e58 100644 --- a/windows/tauri/src/extensions/ui/components/pro-gate.tsx +++ b/windows/tauri/src/extensions/ui/components/pro-gate.tsx @@ -1,6 +1,7 @@ import type { ReactNode } from "react"; import { LockIcon as Lock } from "@/ui/icons"; import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "@/ui/empty"; +import { useTranslation } from "@/i18n/locale-provider"; import { useProFeature } from "../hooks/use-pro-feature"; import { ProBadge } from "./pro-badge"; @@ -10,6 +11,7 @@ interface ProGateProps { } export function ProGate({ children, fallback }: ProGateProps) { + const { t } = useTranslation(); const { hasHostedAi } = useProFeature(); if (hasHostedAi) { @@ -27,10 +29,10 @@ export function ProGate({ children, fallback }: ProGateProps) { - Pro Feature + {t("extensions.proFeature")} - Upgrade to Pro to unlock this feature. + {t("extensions.upgradeToPro")} ); diff --git a/windows/tauri/src/extensions/v0/components/v0-design-system-command.tsx b/windows/tauri/src/extensions/v0/components/v0-design-system-command.tsx index 6f8bdcc91..a34b76c0b 100644 --- a/windows/tauri/src/extensions/v0/components/v0-design-system-command.tsx +++ b/windows/tauri/src/extensions/v0/components/v0-design-system-command.tsx @@ -20,6 +20,7 @@ import { type V0DesignSystemSuggestion, } from "@/extensions/v0/lib/v0-design-systems"; import type { V0DesignSystemProfile } from "@/extensions/v0/types/v0-design-system.types"; +import { useTranslation } from "@/i18n/locale-provider"; import { useSettingsStore } from "@/features/settings/stores/settings.store"; import Badge from "@/ui/badge"; import { @@ -57,17 +58,17 @@ type DesignSystemRow = const NO_DESIGN_SYSTEM_ROW: DesignSystemRow = { kind: "none", id: "", - name: "No design system", - description: "Use v0 defaults", + name: "", + description: "", registryUrl: "", }; -function getNameFromRegistryUrl(registryUrl: string): string { +function getNameFromRegistryUrl(registryUrl: string, fallbackName: string): string { try { const parsed = new URL(registryUrl); return parsed.hostname.replace(/^www\./, ""); } catch { - return registryUrl.replace(/^https?:\/\//, "").replace(/\/.*$/, "") || "Design system"; + return registryUrl.replace(/^https?:\/\//, "").replace(/\/.*$/, "") || fallbackName; } } @@ -115,6 +116,7 @@ export function V0DesignSystemCommandContent({ onBack, onClose, }: V0DesignSystemCommandContentProps) { + const { t } = useTranslation(); const settings = useSettingsStore( useShallow((state) => ({ activeV0DesignSystemId: state.settings.activeV0DesignSystemId, @@ -155,11 +157,15 @@ export function V0DesignSystemCommandContent({ const rows = useMemo( () => [ - NO_DESIGN_SYSTEM_ROW, + { + ...NO_DESIGN_SYSTEM_ROW, + name: t("v0DesignSystem.noneName"), + description: t("v0DesignSystem.noneDescription"), + }, ...settings.v0DesignSystems.map((profile) => ({ ...profile, kind: "profile" as const })), ...visibleSuggestions.map((suggestion) => ({ ...suggestion, kind: "suggestion" as const })), ], - [settings.v0DesignSystems, visibleSuggestions], + [settings.v0DesignSystems, t, visibleSuggestions], ); const filteredRows = useMemo( @@ -214,7 +220,7 @@ export function V0DesignSystemCommandContent({ headers: { Accept: "application/json" }, }); if (!response.ok) { - throw new Error(`Registry directory returned ${response.status}`); + throw new Error(t("v0DesignSystem.registryDirectoryStatus", { status: response.status })); } const directory = await response.json(); @@ -223,10 +229,10 @@ export function V0DesignSystemCommandContent({ } catch (error) { setDirectoryStatus("error"); setDirectoryError( - error instanceof Error ? error.message : "Could not load public registries", + error instanceof Error ? error.message : t("v0DesignSystem.loadPublicFailed"), ); } - }, []); + }, [t]); useEffect(() => { if (!isActive || directoryStatus !== "idle") return; @@ -315,11 +321,12 @@ export function V0DesignSystemCommandContent({ const saveProfile = useCallback(async () => { const registryUrl = registryUrlInput.trim(); if (!registryUrl) { - setFormError("Registry URL is required."); + setFormError(t("v0DesignSystem.registryRequired")); return; } - const name = nameInput.trim() || getNameFromRegistryUrl(registryUrl); + const name = + nameInput.trim() || getNameFromRegistryUrl(registryUrl, t("v0DesignSystem.fallbackName")); const id = getUniqueProfileId(settings.v0DesignSystems, name, registryUrl); const fallbackProfile: V0DesignSystemProfile = { id, @@ -344,6 +351,7 @@ export function V0DesignSystemCommandContent({ persistProfile, registryUrlInput, settings.v0DesignSystems, + t, ]); const removeSelectedProfile = useCallback(() => { @@ -411,13 +419,13 @@ export function V0DesignSystemCommandContent({ setMode("list"); requestAnimationFrame(() => searchInputRef.current?.focus()); }} - aria-label="Back to v0 design systems" + aria-label={t("v0DesignSystem.backToDesignSystems")} >
- Add v0 design system + {t("v0DesignSystem.addTitle")}
@@ -436,14 +444,14 @@ export function V0DesignSystemCommandContent({ value={nameInput} onChange={(event) => setNameInput(event.currentTarget.value)} onKeyDown={handleFormKeyDown} - placeholder="Name" + placeholder={t("v0DesignSystem.namePlaceholder")} size="xs" /> setDescriptionInput(event.currentTarget.value)} onKeyDown={handleFormKeyDown} - placeholder="Notes" + placeholder={t("v0DesignSystem.notesPlaceholder")} size="xs" /> {formError &&
{formError}
} @@ -455,9 +463,11 @@ export function V0DesignSystemCommandContent({ onClick={() => void saveProfile()} disabled={Boolean(savingRegistryUrl)} > - {savingRegistryUrl ? "Saving..." : "Save and use"} + {savingRegistryUrl ? t("ui.saving") : t("v0DesignSystem.saveAndUse")} + + setMode("list")}> + {t("ui.cancel")} - setMode("list")}>Cancel ); @@ -467,7 +477,11 @@ export function V0DesignSystemCommandContent({ <>
- + @@ -476,17 +490,21 @@ export function V0DesignSystemCommandContent({ value={query} onChange={setQuery} onKeyDown={handleListKeyDown} - placeholder="Search v0 design systems..." + placeholder={t("v0DesignSystem.searchPlaceholder")} className="flex-1" /> void loadDirectorySuggestions()} - tooltip="Refresh public registries" + tooltip={t("v0DesignSystem.refreshPublicRegistries")} > - +
@@ -494,7 +512,7 @@ export function V0DesignSystemCommandContent({ {filteredRows.length === 0 ? ( - No design systems found + {t("v0DesignSystem.noFound")} ) : ( filteredRows.map((row, index) => { const isCurrent = row.id === settings.activeV0DesignSystemId; @@ -523,19 +541,19 @@ export function V0DesignSystemCommandContent({
{isAdding ? ( - adding + {t("v0DesignSystem.adding")} ) : isCurrent ? ( - active + {t("v0DesignSystem.active")} ) : row.kind === "profile" ? ( - saved + {t("v0DesignSystem.saved")} ) : row.kind === "suggestion" ? ( - add + {t("v0DesignSystem.add")} ) : null} @@ -547,25 +565,25 @@ export function V0DesignSystemCommandContent({ - Add registry + {t("v0DesignSystem.addRegistry")} void loadDirectorySuggestions()}> - Refresh + {t("ui.refresh")} - Remove selected + {t("v0DesignSystem.removeSelected")} {directoryStatus === "loading" - ? "Loading..." + ? t("ui.loading") : directoryStatus === "error" ? directoryError - : `${visibleSuggestions.length} public`} + : t("v0DesignSystem.publicCount", { count: visibleSuggestions.length })} diff --git a/windows/tauri/src/extensions/viewers/csv/csv-preview.tsx b/windows/tauri/src/extensions/viewers/csv/csv-preview.tsx index 0eac31088..630e711ac 100644 --- a/windows/tauri/src/extensions/viewers/csv/csv-preview.tsx +++ b/windows/tauri/src/extensions/viewers/csv/csv-preview.tsx @@ -4,6 +4,7 @@ import { useBufferStore } from "@/features/editor/stores/buffer.store"; import { useEditorSettingsStore } from "@/features/editor/stores/settings.store"; import { hasTextContent } from "@/features/panes/types/pane-content.types"; import { useSettingsStore } from "@/features/settings/stores/settings.store"; +import { useTranslation } from "@/i18n/locale-provider"; import { Button } from "@/ui/button"; import Select from "@/ui/select"; import { TableView } from "./csv-table-view"; @@ -27,6 +28,7 @@ function autodetectDelimiter(text: string): Delim { } export function CsvPreview() { + const { t } = useTranslation(); const sourceContent = useBufferStore((state) => { const activeBuffer = state.activeBufferId ? state.buffers.find((buffer) => buffer.id === state.activeBufferId) @@ -94,22 +96,22 @@ export function CsvPreview() { htmlFor="csv-delimiter" className="font-sans mr-1 text-subtle-foreground ui-text-sm" > - Delimiter + {t("csv.delimiter")} setSearchTerm(event.target.value)} onKeyDown={(event) => { @@ -236,7 +239,7 @@ function ContextSelectorDropdownContent({ size="xs" leftIcon={Search} className="w-full" - aria-label="Search context" + aria-label={t("ai.searchContext")} />
{shouldShowContextResults ? ( @@ -251,7 +254,7 @@ function ContextSelectorDropdownContent({ setSelectedContextIndex(filteredContextBuffers.length + index) } onResultsChange={setVisibleFileResults} - emptyLabel="No matching context found" + emptyLabel={t("ai.noMatchingContextFound")} compact showSearchInput={false} listClassName="max-h-66" @@ -259,7 +262,7 @@ function ContextSelectorDropdownContent({ filteredContextBuffers.length > 0 ? ( <>
- Open tabs + {t("ai.openTabs")}
{filteredContextBuffers.map((buffer) => { const index = filteredContextBuffers.indexOf(buffer); @@ -293,12 +296,12 @@ function ContextSelectorDropdownContent({ {buffer.name} - {getBufferContextDescription(buffer)} + {getBufferContextDescription(buffer, t)} {isSelected && ( - added + {t("ai.added")} )} diff --git a/windows/tauri/src/features/ai/components/selectors/model-selector.tsx b/windows/tauri/src/features/ai/components/selectors/model-selector.tsx index 42eb77d91..53201a57e 100644 --- a/windows/tauri/src/features/ai/components/selectors/model-selector.tsx +++ b/windows/tauri/src/features/ai/components/selectors/model-selector.tsx @@ -2,6 +2,7 @@ import { LockIcon as Lock, WarningCircleIcon as WarningCircle } from "@/ui/icons import { useAIModelOptions } from "@/features/ai/hooks/use-ai-model-options"; import { ProBadge } from "@/extensions/ui/components/pro-badge"; import { Alert, AlertDescription } from "@/ui/alert"; +import { useTranslation } from "@/i18n/locale-provider"; import Select from "@/ui/select"; import { cn } from "@/utils/cn"; @@ -30,6 +31,7 @@ export function ModelSelector({ onOpenChange, tooltip, }: ModelSelectorProps) { + const { t } = useTranslation(); const isComposer = appearance === "composer"; const { availableModels, currentModelName, hasHostedAi, isCustomProvider, modelFetchError } = useAIModelOptions(providerId, modelId, onChange); @@ -50,13 +52,13 @@ export function ModelSelector({ }; })} placeholder={currentModelName} - aria-label="Select AI model" + aria-label={t("ai.selectAiModel")} searchable searchableTrigger={isComposer ? "input" : "menu"} openDirection={isComposer ? "up" : "down"} allowCustomValue={isCustomProvider} - customValueLabel={(customValue) => `Use ${customValue}`} - emptyLabel={isCustomProvider ? "Type a model name and press Enter" : "No models found"} + customValueLabel={(customValue) => t("ai.useCustomValue", { value: customValue })} + emptyLabel={isCustomProvider ? t("ai.typeModelName") : t("ai.noModelsFound")} hideChevron={isComposer} size="xs" variant={isComposer ? "ghost" : "default"} diff --git a/windows/tauri/src/features/ai/components/selectors/provider-selector.tsx b/windows/tauri/src/features/ai/components/selectors/provider-selector.tsx index 730e30a3e..372303317 100644 --- a/windows/tauri/src/features/ai/components/selectors/provider-selector.tsx +++ b/windows/tauri/src/features/ai/components/selectors/provider-selector.tsx @@ -3,6 +3,7 @@ import { useAvailableProviders, useProviderById, } from "@/features/ai/hooks/use-available-providers"; +import { useTranslation } from "@/i18n/locale-provider"; import Select from "@/ui/select"; import { cn } from "@/utils/cn"; @@ -29,6 +30,7 @@ export function ProviderSelector({ onOpenChange, tooltip, }: ProviderSelectorProps) { + const { t } = useTranslation(); const providers = useAvailableProviders(); const currentProvider = useProviderById(providerId); const isComposer = appearance === "composer"; @@ -49,8 +51,8 @@ export function ProviderSelector({ /> ), }))} - placeholder={currentProvider?.name || providerId || "Select provider"} - aria-label="Select AI provider" + placeholder={currentProvider?.name || providerId || t("ai.selectProvider")} + aria-label={t("ai.selectAiProvider")} searchable searchableTrigger={isComposer ? "input" : "menu"} hideChevron={isComposer} diff --git a/windows/tauri/src/features/ai/components/skills/skills-command.tsx b/windows/tauri/src/features/ai/components/skills/skills-command.tsx index cd8d46e33..427dafd6c 100644 --- a/windows/tauri/src/features/ai/components/skills/skills-command.tsx +++ b/windows/tauri/src/features/ai/components/skills/skills-command.tsx @@ -26,6 +26,7 @@ import { import { fuzzyScore } from "@/features/global-search/utils/fuzzy-search"; import { useSettingsStore } from "@/features/settings/stores/settings.store"; import { useSettingsSyncStore } from "@/features/settings/stores/settings-sync.store"; +import { useTranslation } from "@/i18n/locale-provider"; import type { AIChatSkill, MarketplaceSkill } from "@/features/ai/types/skills.types"; import { Button } from "@/ui/button"; import Command, { @@ -59,11 +60,11 @@ function createSkillId() { return `skill-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`; } -function getSyncLabel(enabled: boolean, status: string) { - if (!enabled) return "Not synced"; - if (status === "syncing") return "Syncing"; - if (status === "error") return "Sync paused"; - return "Synced"; +function getSyncLabel(enabled: boolean, status: string, t: (key: string) => string) { + if (!enabled) return t("ai.notSynced"); + if (status === "syncing") return t("ai.syncing"); + if (status === "error") return t("ai.syncPaused"); + return t("ai.synced"); } function getSyncIcon(enabled: boolean, status: string) { @@ -79,6 +80,7 @@ export function SkillsCommand({ onSelectSkill, initialView = "list", }: SkillsCommandProps) { + const { t } = useTranslation(); const inputRef = useRef(null); const titleInputRef = useRef(null); const resultsRef = useRef(null); @@ -352,39 +354,39 @@ export function SkillsCommand({ ref={inputRef} value={query} onChange={setQuery} - placeholder={view === "browse" ? "Search available skills..." : "Search skills..."} + placeholder={view === "browse" ? t("ai.searchAvailableSkills") : t("ai.searchSkills")} /> {view === "list" ? ( - New + {t("ai.new")} ) : ( setView("list")}> - My skills + {t("ai.mySkills")} )} - Browse + {t("ai.browse")} {view === "browse" ? ( isLoadingMarketplace ? ( - Loading available skills... + {t("ai.loadingAvailableSkills")} ) : marketplaceSkills.length === 0 ? (
-
No published skills yet
+
{t("ai.noPublishedSkillsYet")}
- Published skills will appear here once the Lithe skills registry is available. + {t("ai.publishedSkillsWillAppear")}
) : filteredMarketplaceSkills.length === 0 ? ( - No available skills match "{query}" + {t("ai.noAvailableSkillsMatch", { query })} ) : ( filteredMarketplaceSkills.map((skill, index) => { const isSelected = selectedIndex === index; @@ -432,7 +434,7 @@ export function SkillsCommand({ } }} > - {isInstalled ? "Added" : "Add"} + {isInstalled ? t("ai.added") : t("ai.add")} } /> @@ -440,9 +442,9 @@ export function SkillsCommand({ }) ) ) : skills.length === 0 ? ( - No skills yet + {t("ai.noSkillsYet")} ) : filteredSkills.length === 0 ? ( - No skills match "{query}" + {t("ai.noSkillsMatch", { query })} ) : ( filteredSkills.map((skill, index) => { const isSelected = selectedIndex === index; @@ -464,10 +466,10 @@ export function SkillsCommand({ accessory={ <> {skill.source === "marketplace" ? ( - Marketplace + {t("ai.marketplace")} ) : null} {hasLocalOverride ? ( - Local override + {t("ai.localOverride")} ) : null} } @@ -481,8 +483,8 @@ export function SkillsCommand({ openSkillEditor(skill); }} className="opacity-0 focus:opacity-100 group-hover:opacity-100" - tooltip="Edit skill" - aria-label={`Edit ${skill.title}`} + tooltip={t("ai.editSkill")} + aria-label={t("ai.editNamedSkill", { title: skill.title })} size={isComposerAttached ? "icon-xs" : "icon"} > @@ -495,8 +497,8 @@ export function SkillsCommand({ void handleDelete(skill.id); }} className="opacity-0 hover:bg-destructive/10 hover:text-destructive focus:opacity-100 group-hover:opacity-100" - tooltip="Delete skill" - aria-label={`Delete ${skill.title}`} + tooltip={t("ai.deleteSkill")} + aria-label={t("ai.deleteNamedSkill", { title: skill.title })} size={isComposerAttached ? "icon-xs" : "icon"} > @@ -512,7 +514,7 @@ export function SkillsCommand({ - {getSyncLabel(syncEnabled, syncStatus)} + {getSyncLabel(syncEnabled, syncStatus, t)} @@ -521,7 +523,7 @@ export function SkillsCommand({
- {editingSkillId ? "Edit skill" : "New skill"} + {editingSkillId ? t("ai.editSkill") : t("ai.newSkill")}
{(() => { const editingSkill = skills.find((skill) => skill.id === editingSkillId); @@ -529,8 +531,9 @@ export function SkillsCommand({ return (
- Marketplace skill - {hasSkillLocalOverride(editingSkill) ? " with local override" : ""} + {hasSkillLocalOverride(editingSkill) + ? t("ai.marketplaceSkillWithLocalOverride") + : t("ai.marketplaceSkill")}
); })()} @@ -543,14 +546,14 @@ export function SkillsCommand({ className="font-sans ui-text-base text-subtle-foreground" htmlFor="ai-skill-title" > - Title + {t("ai.title")} setTitle(event.target.value)} - placeholder="Code review checklist" + placeholder={t("ai.skillTitlePlaceholder")} maxLength={120} size="sm" /> @@ -561,13 +564,13 @@ export function SkillsCommand({ className="font-sans ui-text-base text-subtle-foreground" htmlFor="ai-skill-content" > - Markdown + {t("ai.markdown")}