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,6 +1,7 @@
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Image, Text, View } from "react-native";
import { StyleSheet } from "react-native-unistyles";
import { useTranslation } from "react-i18next";
import { createNameId } from "mnemonic-id";
import { AdaptiveModalSheet, type SheetHeader } from "@/components/adaptive-modal-sheet";
import { Composer } from "@/composer";
@@ -96,8 +97,13 @@ async function callWorkspaceCreation({
return connectedClient.openProject(input.cwd);
}
function failureMessageForCreationMethod(method: "create_worktree" | "open_project") {
return method === "create_worktree" ? "Failed to create worktree" : "Failed to open project";
function failureMessageForCreationMethod(
method: "create_worktree" | "open_project",
t: ReturnType<typeof useTranslation>["t"],
) {
return method === "create_worktree"
? t("workspaceSetup.errors.failedCreateWorktree")
: t("workspaceSetup.errors.failedOpenProject");
}
function buildCreateAgentOptions({
@@ -140,6 +146,7 @@ function buildCreateAgentOptions({
}
export function WorkspaceSetupDialog() {
const { t } = useTranslation();
const toast = useToast();
const pendingWorkspaceSetup = useWorkspaceSetupStore((state) => state.pendingWorkspaceSetup);
const clearWorkspaceSetup = useWorkspaceSetupStore((state) => state.clearWorkspaceSetup);
@@ -170,7 +177,7 @@ export function WorkspaceSetupDialog() {
});
const composerState = chatDraft.composerState;
if (!composerState && pendingWorkspaceSetup) {
throw new Error("Workspace setup composer state is required");
throw new Error(t("workspaceSetup.errors.composerStateRequired"));
}
const { icon: projectIcon } = useProjectIconQuery({
@@ -218,15 +225,15 @@ export function WorkspaceSetupDialog() {
const withConnectedClient = useCallback(() => {
if (!client || !isConnected) {
throw new Error("Host is not connected");
throw new Error(t("workspaceSetup.errors.hostDisconnected"));
}
return client;
}, [client, isConnected]);
}, [client, isConnected, t]);
const ensureWorkspace = useCallback(
async (input: { cwd: string; attachments: MessagePayload["attachments"] }) => {
if (!pendingWorkspaceSetup) {
throw new Error("No workspace setup is pending");
throw new Error(t("workspaceSetup.errors.pendingRequired"));
}
if (createdWorkspace) {
@@ -242,7 +249,7 @@ export function WorkspaceSetupDialog() {
if (payload.error || !payload.workspace) {
throw new Error(
payload.error ?? failureMessageForCreationMethod(pendingWorkspaceSetup.creationMethod),
payload.error ?? failureMessageForCreationMethod(pendingWorkspaceSetup.creationMethod, t),
);
}
@@ -259,6 +266,7 @@ export function WorkspaceSetupDialog() {
mergeWorkspaces,
pendingWorkspaceSetup,
setHasHydratedWorkspaces,
t,
withConnectedClient,
],
);
@@ -284,10 +292,10 @@ export function WorkspaceSetupDialog() {
const ensuredWorkspace = await ensureWorkspace({ cwd, attachments });
const connectedClient = withConnectedClient();
if (!composerState) {
throw new Error("Workspace setup composer state is required");
throw new Error(t("workspaceSetup.errors.composerStateRequired"));
}
if (!composerState.selectedProvider) {
throw new Error("Select a model");
throw new Error(t("workspaceSetup.errors.selectModel"));
}
const wirePayload = splitComposerAttachmentsForSubmit(attachments);
@@ -334,6 +342,7 @@ export function WorkspaceSetupDialog() {
serverId,
setAgents,
ensureWorkspace,
t,
toast,
withConnectedClient,
],
@@ -392,8 +401,8 @@ export function WorkspaceSetupDialog() {
);
const sheetHeader = useMemo<SheetHeader>(
() => ({ title: "Create workspace", subtitle: subtitleContent }),
[subtitleContent],
() => ({ title: t("workspaceSetup.title"), subtitle: subtitleContent }),
[subtitleContent, t],
);
if (!pendingWorkspaceSetup || !sourceDirectory) {