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, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { CheckCircle2, ChevronRight, CircleAlert, SquareTerminal } from "lucide-react-native";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Pressable,
|
||||
@@ -25,6 +26,7 @@ function useSetupPanelDescriptor(
|
||||
target: { kind: "setup"; workspaceId: string },
|
||||
context: { serverId: string; workspaceId: string },
|
||||
): PanelDescriptor {
|
||||
const { t } = useTranslation();
|
||||
const key = buildWorkspaceTabPersistenceKey({
|
||||
serverId: context.serverId,
|
||||
workspaceId: target.workspaceId,
|
||||
@@ -33,8 +35,8 @@ function useSetupPanelDescriptor(
|
||||
|
||||
if (snapshot?.status === "completed") {
|
||||
return {
|
||||
label: "Setup",
|
||||
subtitle: "Setup completed",
|
||||
label: t("workspace.setup.descriptor.label"),
|
||||
subtitle: t("workspace.setup.descriptor.completed"),
|
||||
titleState: "ready",
|
||||
icon: CheckCircle2,
|
||||
statusBucket: null,
|
||||
@@ -43,8 +45,8 @@ function useSetupPanelDescriptor(
|
||||
|
||||
if (snapshot?.status === "failed") {
|
||||
return {
|
||||
label: "Setup",
|
||||
subtitle: "Setup failed",
|
||||
label: t("workspace.setup.descriptor.label"),
|
||||
subtitle: t("workspace.setup.descriptor.failed"),
|
||||
titleState: "ready",
|
||||
icon: CircleAlert,
|
||||
statusBucket: null,
|
||||
@@ -52,8 +54,8 @@ function useSetupPanelDescriptor(
|
||||
}
|
||||
|
||||
return {
|
||||
label: "Setup",
|
||||
subtitle: "Workspace setup",
|
||||
label: t("workspace.setup.descriptor.label"),
|
||||
subtitle: t("workspace.setup.descriptor.workspace"),
|
||||
titleState: "ready",
|
||||
icon: SquareTerminal,
|
||||
statusBucket: snapshot?.status === "running" ? "running" : null,
|
||||
@@ -108,11 +110,14 @@ function resolveAutoExpandIndex(commands: { index: number; status: string }[]):
|
||||
return null;
|
||||
}
|
||||
|
||||
function resolveSetupStatusLabel(status: string | undefined): string {
|
||||
if (status === "running") return "Running";
|
||||
if (status === "completed") return "Completed";
|
||||
if (status === "failed") return "Failed";
|
||||
return "Waiting for setup output";
|
||||
function resolveSetupStatusLabel(
|
||||
status: string | undefined,
|
||||
labels: Record<CommandStatus | "waiting", string>,
|
||||
): string {
|
||||
if (status === "running") return labels.running;
|
||||
if (status === "completed") return labels.completed;
|
||||
if (status === "failed") return labels.failed;
|
||||
return labels.waiting;
|
||||
}
|
||||
|
||||
function resolveCommandLog(
|
||||
@@ -150,6 +155,7 @@ function buildCommandRowState(args: BuildCommandRowPropsArgs) {
|
||||
}
|
||||
|
||||
function SetupPanel() {
|
||||
const { t } = useTranslation();
|
||||
const { serverId, target } = usePaneContext();
|
||||
invariant(target.kind === "setup", "SetupPanel requires setup target");
|
||||
|
||||
@@ -214,7 +220,16 @@ function SetupPanel() {
|
||||
}, []);
|
||||
|
||||
const autoExpandIndex = resolveAutoExpandIndex(commands);
|
||||
const statusLabel = resolveSetupStatusLabel(snapshot?.status);
|
||||
const statusLabels = useMemo(
|
||||
() => ({
|
||||
running: t("workspace.setup.status.running"),
|
||||
completed: t("workspace.setup.status.completed"),
|
||||
failed: t("workspace.setup.status.failed"),
|
||||
waiting: t("workspace.setup.status.waiting"),
|
||||
}),
|
||||
[t],
|
||||
);
|
||||
const statusLabel = resolveSetupStatusLabel(snapshot?.status, statusLabels);
|
||||
|
||||
return (
|
||||
<ScrollView
|
||||
@@ -230,7 +245,7 @@ function SetupPanel() {
|
||||
{isWaiting ? (
|
||||
<View style={styles.waitingContainer}>
|
||||
<ThemedActivityIndicator size="large" uniProps={foregroundMutedColorMapping} />
|
||||
<Text style={styles.waitingText}>Setting up workspace...</Text>
|
||||
<Text style={styles.waitingText}>{t("workspace.setup.waiting")}</Text>
|
||||
</View>
|
||||
) : null}
|
||||
{!isWaiting && hasNoSetupCommands ? (
|
||||
@@ -238,9 +253,9 @@ function SetupPanel() {
|
||||
<Text
|
||||
style={styles.emptyText}
|
||||
accessible
|
||||
accessibilityLabel="No setup commands ran for this workspace"
|
||||
accessibilityLabel={t("workspace.setup.accessibility.noCommands")}
|
||||
>
|
||||
No setup commands ran for this workspace.
|
||||
{t("workspace.setup.empty.noCommands")}
|
||||
</Text>
|
||||
</View>
|
||||
) : null}
|
||||
@@ -303,6 +318,7 @@ function SetupCommandRow({
|
||||
errorMessage,
|
||||
onToggle,
|
||||
}: SetupCommandRowProps) {
|
||||
const { t } = useTranslation();
|
||||
const handlePress = useCallback(() => {
|
||||
if (!isExpandable) return;
|
||||
onToggle(command.index, isAutoExpanded);
|
||||
@@ -348,7 +364,7 @@ function SetupCommandRow({
|
||||
showsVerticalScrollIndicator
|
||||
testID="workspace-setup-log"
|
||||
accessible
|
||||
accessibilityLabel="Workspace setup log"
|
||||
accessibilityLabel={t("workspace.setup.accessibility.log")}
|
||||
>
|
||||
<Text selectable dataSet={CODE_SURFACE_DATASET} style={styles.logText}>
|
||||
{processedLog}
|
||||
@@ -359,9 +375,9 @@ function SetupCommandRow({
|
||||
style={styles.logScrollContent}
|
||||
testID="workspace-setup-log"
|
||||
accessible
|
||||
accessibilityLabel="Workspace setup log"
|
||||
accessibilityLabel={t("workspace.setup.accessibility.log")}
|
||||
>
|
||||
<Text style={styles.emptyLogText}>No output</Text>
|
||||
<Text style={styles.emptyLogText}>{t("workspace.setup.log.noOutput")}</Text>
|
||||
</View>
|
||||
)}
|
||||
{hasError && errorMessage ? (
|
||||
@@ -394,6 +410,7 @@ function SetupCommandChevron({ showDetail }: { showDetail: boolean }) {
|
||||
}
|
||||
|
||||
function StandaloneLogView({ commands, log }: { commands: SetupCommand[]; log: string }) {
|
||||
const { t } = useTranslation();
|
||||
if (commands.length !== 0 || log.trim().length === 0) return null;
|
||||
return (
|
||||
<ScrollView
|
||||
@@ -402,7 +419,7 @@ function StandaloneLogView({ commands, log }: { commands: SetupCommand[]; log: s
|
||||
showsVerticalScrollIndicator
|
||||
testID="workspace-setup-log"
|
||||
accessible
|
||||
accessibilityLabel="Workspace setup log"
|
||||
accessibilityLabel={t("workspace.setup.accessibility.log")}
|
||||
>
|
||||
<Text selectable dataSet={CODE_SURFACE_DATASET} style={styles.logText}>
|
||||
{log}
|
||||
|
||||
Reference in New Issue
Block a user