Complete client UI i18n migration

Adds client-side i18n support, language settings, translated UI copy, and locale parity coverage.
This commit is contained in:
chyendongnhanh338
2026-06-11 14:15:35 +08:00
committed by GitHub
parent 41cf070fd8
commit 08ebe5e7e1
199 changed files with 16426 additions and 1821 deletions
@@ -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",
});
},