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,7 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import type { TFunction } from "i18next";
import { useTranslation } from "react-i18next";
import type { AutocompleteOption } from "@/components/ui/autocomplete";
import {
useAgentCommandsQuery,
@@ -115,13 +117,14 @@ function mapDirectorySuggestionsToEntries(payload: {
}));
}
function mapCommandToOption(entry: AvailableCommand): AgentAutocompleteOption {
function mapCommandToOption(entry: AvailableCommand, t: TFunction): AgentAutocompleteOption {
const command = entry.command;
const base = {
id: command.name,
label: `/${command.name}`,
detail: command.argumentHint || undefined,
description: command.description,
description:
entry.source === "client" ? t(entry.command.descriptionKey) : entry.command.description,
kind: "command" as const,
};
if (entry.source === "client") {
@@ -148,6 +151,7 @@ interface BuildAutocompleteOptionsInput {
activeSlashCommand: SlashCommandRange | null;
activeFileMention: FileMentionRange | null;
fileSuggestions: DirectorySuggestionEntry[];
t: TFunction;
}
function buildCommandAutocompleteOptions(input: BuildAutocompleteOptionsInput) {
@@ -177,7 +181,7 @@ function buildCommandAutocompleteOptions(input: BuildAutocompleteOptionsInput) {
input.commandFilterQuery,
);
const orderedMatches = orderAutocompleteOptions(matches);
return orderedMatches.map(mapCommandToOption);
return orderedMatches.map((entry) => mapCommandToOption(entry, input.t));
}
const activeFileMention = input.activeFileMention;
@@ -258,9 +262,12 @@ function resolveAutocompleteErrorMessage(args: {
isCommandError: boolean;
commandError: Error | null;
fileSuggestionsError: unknown;
t: TFunction;
}): string | undefined {
if (args.mode === "command") {
return args.isCommandError ? (args.commandError?.message ?? "Failed to load") : undefined;
return args.isCommandError
? (args.commandError?.message ?? args.t("agentAutocomplete.failedToLoad"))
: undefined;
}
if (args.mode === "file") {
return args.fileSuggestionsError instanceof Error
@@ -271,6 +278,7 @@ function resolveAutocompleteErrorMessage(args: {
}
export function useAgentAutocomplete(input: UseAgentAutocompleteInput): AgentAutocompleteResult {
const { t } = useTranslation();
const {
userInput,
cursorIndex,
@@ -366,7 +374,7 @@ export function useAgentAutocomplete(input: UseAgentAutocompleteInput): AgentAut
],
queryFn: async (): Promise<DirectorySuggestionEntry[]> => {
if (!client) {
throw new Error("Daemon client unavailable");
throw new Error(t("common.errors.daemonClientUnavailable"));
}
const response = await client.getDirectorySuggestions({
cwd: autocompleteCwd,
@@ -402,6 +410,7 @@ export function useAgentAutocomplete(input: UseAgentAutocompleteInput): AgentAut
isDraftContext,
isVisible,
mode,
t,
}),
[
activeFileMention,
@@ -412,6 +421,7 @@ export function useAgentAutocomplete(input: UseAgentAutocompleteInput): AgentAut
isDraftContext,
isVisible,
mode,
t,
],
);
@@ -488,10 +498,15 @@ export function useAgentAutocomplete(input: UseAgentAutocompleteInput): AgentAut
isCommandError: isError,
commandError: error,
fileSuggestionsError: fileSuggestionsQuery.error,
t,
});
const loadingText = mode === "file" ? "Searching workspace..." : "Loading commands...";
const emptyText = mode === "file" ? "No files or directories found" : "No commands found";
const loadingText =
mode === "file"
? t("agentAutocomplete.searchingWorkspace")
: t("agentAutocomplete.loadingCommands");
const emptyText =
mode === "file" ? t("agentAutocomplete.noFiles") : t("agentAutocomplete.noCommands");
return {
isVisible,