Complete client UI i18n migration
Adds client-side i18n support, language settings, translated UI copy, and locale parity coverage.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useCallback } from "react";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
type DesktopDaemonStatus,
|
||||
startDesktopDaemon,
|
||||
@@ -38,6 +39,7 @@ interface UseBuiltInDaemonManagementResult {
|
||||
export function useBuiltInDaemonManagement(
|
||||
input: UseBuiltInDaemonManagementInput,
|
||||
): UseBuiltInDaemonManagementResult {
|
||||
const { t } = useTranslation();
|
||||
const { daemonStatus, settings, updateSettings, setStatus, refreshStatus } = input;
|
||||
const reportError = useDesktopIpcErrorReporter();
|
||||
const { mutate: toggleDaemonManagement, isPending: isUpdating } = useMutation<
|
||||
@@ -50,11 +52,10 @@ export function useBuiltInDaemonManagement(
|
||||
const result = await executeDaemonManagementToggle(wasManagingDaemon, daemonStatus, {
|
||||
confirm: () =>
|
||||
confirmDialog({
|
||||
title: "Pause built-in daemon",
|
||||
message:
|
||||
"This will stop the built-in daemon immediately. Running agents and terminals connected to the built-in daemon will be stopped.",
|
||||
confirmLabel: "Pause and stop",
|
||||
cancelLabel: "Cancel",
|
||||
title: t("desktop.daemon.management.pauseTitle"),
|
||||
message: t("desktop.daemon.management.pauseMessage"),
|
||||
confirmLabel: t("desktop.daemon.management.pauseAndStop"),
|
||||
cancelLabel: t("common.actions.cancel"),
|
||||
destructive: true,
|
||||
}),
|
||||
persistSettings: (next) => updateSettings(next) as Promise<void>,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useCallback } from "react";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
getDesktopDaemonLogs,
|
||||
getDesktopDaemonStatus,
|
||||
@@ -17,6 +18,7 @@ interface DaemonStatusData {
|
||||
}
|
||||
|
||||
export function useDaemonStatus() {
|
||||
const { t } = useTranslation();
|
||||
const queryClient = useQueryClient();
|
||||
const enabled = shouldUseDesktopDaemon();
|
||||
|
||||
@@ -33,7 +35,7 @@ export function useDaemonStatus() {
|
||||
});
|
||||
useDesktopIpcQueryErrorToast({
|
||||
error: query.error,
|
||||
message: "Unable to load desktop daemon status.",
|
||||
message: t("desktop.daemon.loadFailed"),
|
||||
logLabel: "[DesktopDaemon] Failed to load daemon status",
|
||||
});
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import React from "react";
|
||||
import { act, renderHook, waitFor } from "@testing-library/react";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { i18n } from "@/i18n/i18next";
|
||||
import { useCliInstall, useSkillsStatus } from "./use-install-status";
|
||||
|
||||
const toast = vi.hoisted(() => ({
|
||||
@@ -54,6 +55,7 @@ describe("useCliInstall", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
void i18n.changeLanguage("en");
|
||||
vi.restoreAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
@@ -89,6 +91,28 @@ describe("useCliInstall", () => {
|
||||
expect(toast.error).toHaveBeenCalledWith("Unable to install the Paseo CLI.");
|
||||
expect(console.error).toHaveBeenCalledWith("[Integrations] Failed to install CLI", error);
|
||||
});
|
||||
|
||||
it("uses the active language for CLI install errors", async () => {
|
||||
await i18n.changeLanguage("zh-CN");
|
||||
const error = new Error("Missing IPC handler");
|
||||
desktopDaemon.getCliInstallStatus.mockResolvedValue({ installed: false });
|
||||
desktopDaemon.installCli.mockRejectedValue(error);
|
||||
const { result } = renderDesktopHook(() => useCliInstall());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.status).toEqual({ installed: false });
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.install();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.error).toBe(error);
|
||||
});
|
||||
|
||||
expect(toast.error).toHaveBeenCalledWith("无法安装 Paseo CLI。");
|
||||
});
|
||||
});
|
||||
|
||||
describe("useSkillsStatus", () => {
|
||||
@@ -97,6 +121,7 @@ describe("useSkillsStatus", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
void i18n.changeLanguage("en");
|
||||
vi.restoreAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useCallback } from "react";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
getCliInstallStatus,
|
||||
getSkillsStatus,
|
||||
@@ -29,6 +30,7 @@ interface DesktopInstallHookResult {
|
||||
}
|
||||
|
||||
export function useCliInstall(): DesktopInstallHookResult {
|
||||
const { t } = useTranslation();
|
||||
const queryClient = useQueryClient();
|
||||
const reportError = useDesktopIpcErrorReporter();
|
||||
const enabled = shouldUseDesktopDaemon();
|
||||
@@ -42,7 +44,7 @@ export function useCliInstall(): DesktopInstallHookResult {
|
||||
const { data: installStatus, error: statusError, isLoading, refetch } = statusQuery;
|
||||
useDesktopIpcQueryErrorToast({
|
||||
error: statusQuery.error,
|
||||
message: "Unable to check CLI install status.",
|
||||
message: t("desktop.integrations.cli.statusFailed"),
|
||||
logLabel: "[Integrations] Failed to load CLI status",
|
||||
});
|
||||
|
||||
@@ -51,7 +53,7 @@ export function useCliInstall(): DesktopInstallHookResult {
|
||||
onError: (error) => {
|
||||
reportError({
|
||||
error,
|
||||
message: "Unable to install the Paseo CLI.",
|
||||
message: t("desktop.integrations.cli.installFailed"),
|
||||
logLabel: "[Integrations] Failed to install CLI",
|
||||
});
|
||||
},
|
||||
@@ -88,6 +90,7 @@ export interface SkillsStatusHookResult {
|
||||
}
|
||||
|
||||
export function useSkillsStatus(): SkillsStatusHookResult {
|
||||
const { t } = useTranslation();
|
||||
const queryClient = useQueryClient();
|
||||
const reportError = useDesktopIpcErrorReporter();
|
||||
const enabled = shouldUseDesktopDaemon();
|
||||
@@ -101,7 +104,7 @@ export function useSkillsStatus(): SkillsStatusHookResult {
|
||||
const { data: status, error: statusError, isLoading, refetch } = statusQuery;
|
||||
useDesktopIpcQueryErrorToast({
|
||||
error: statusQuery.error,
|
||||
message: "Unable to check orchestration skills status.",
|
||||
message: t("desktop.integrations.skills.statusFailed"),
|
||||
logLabel: "[Integrations] Failed to load skills status",
|
||||
});
|
||||
|
||||
@@ -117,7 +120,7 @@ export function useSkillsStatus(): SkillsStatusHookResult {
|
||||
onError: (error) => {
|
||||
reportError({
|
||||
error,
|
||||
message: "Unable to install orchestration skills.",
|
||||
message: t("desktop.integrations.skills.installFailed"),
|
||||
logLabel: "[Integrations] Failed to install skills",
|
||||
});
|
||||
},
|
||||
@@ -129,7 +132,7 @@ export function useSkillsStatus(): SkillsStatusHookResult {
|
||||
onError: (error) => {
|
||||
reportError({
|
||||
error,
|
||||
message: "Unable to update orchestration skills.",
|
||||
message: t("desktop.integrations.skills.updateFailed"),
|
||||
logLabel: "[Integrations] Failed to update skills",
|
||||
});
|
||||
},
|
||||
@@ -141,7 +144,7 @@ export function useSkillsStatus(): SkillsStatusHookResult {
|
||||
onError: (error) => {
|
||||
reportError({
|
||||
error,
|
||||
message: "Unable to uninstall orchestration skills.",
|
||||
message: t("desktop.integrations.skills.uninstallFailed"),
|
||||
logLabel: "[Integrations] Failed to uninstall skills",
|
||||
});
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user