feat(ui): localize UI strings
Converts hardcoded UI copy to i18n keys across the app, adds global translation for non-component modules, and splits the English catalog into feature modules with duplicate-key detection.
This commit is contained in:
@@ -3,6 +3,7 @@ import type { SupportMeta } from "../../../server/src/api-types"
|
||||
import { getServerMeta } from "../lib/server-meta"
|
||||
import { showToastNotification, ToastHandle } from "../lib/notifications"
|
||||
import { getLogger } from "../lib/logger"
|
||||
import { tGlobal } from "../lib/i18n"
|
||||
import { hasInstances, showFolderSelection } from "./ui"
|
||||
|
||||
const log = getLogger("actions")
|
||||
@@ -42,16 +43,16 @@ function ensureVisibilityEffect() {
|
||||
if (!activeToast || activeToastKey !== key) {
|
||||
dismissActiveToast()
|
||||
activeToast = showToastNotification({
|
||||
title: support.message ?? "Upgrade required",
|
||||
title: support.message ?? tGlobal("releases.upgradeRequired.title"),
|
||||
message: support.latestServerVersion
|
||||
? `Update to CodeNomad ${support.latestServerVersion} to use the latest UI.`
|
||||
: "Update CodeNomad to use the latest UI.",
|
||||
? tGlobal("releases.upgradeRequired.message.withVersion", { version: support.latestServerVersion })
|
||||
: tGlobal("releases.upgradeRequired.message.noVersion"),
|
||||
variant: "info",
|
||||
duration: Number.POSITIVE_INFINITY,
|
||||
position: "bottom-right",
|
||||
action: support.latestServerUrl
|
||||
? {
|
||||
label: "Get update",
|
||||
label: tGlobal("releases.upgradeRequired.action.getUpdate"),
|
||||
href: support.latestServerUrl,
|
||||
}
|
||||
: undefined,
|
||||
|
||||
@@ -34,6 +34,7 @@ import { createClientSession, mapSdkSessionStatus, type Session, type SessionSta
|
||||
import { sessions, setSessions, syncInstanceSessionIndicator, withSession } from "./session-state"
|
||||
import { normalizeMessagePart } from "./message-v2/normalizers"
|
||||
import { updateSessionInfo } from "./message-v2/session-info"
|
||||
import { tGlobal } from "../lib/i18n"
|
||||
|
||||
import { loadMessages } from "./session-api"
|
||||
import {
|
||||
@@ -308,7 +309,7 @@ function handleSessionUpdate(instanceId: string, event: EventSessionUpdated): vo
|
||||
const newSession = {
|
||||
id: info.id,
|
||||
instanceId,
|
||||
title: info.title || "Untitled",
|
||||
title: info.title || tGlobal("sessionList.session.untitled"),
|
||||
parentId: info.parentID || null,
|
||||
agent: "",
|
||||
model: {
|
||||
@@ -415,10 +416,11 @@ function handleSessionCompacted(instanceId: string, event: EventSessionCompacted
|
||||
const label = session?.title?.trim() ? session.title : sessionID
|
||||
const instanceFolder = instances().get(instanceId)?.folder ?? instanceId
|
||||
const instanceName = instanceFolder.split(/[\\/]/).filter(Boolean).pop() ?? instanceFolder
|
||||
const displayLabel = label ? `"${label}"` : sessionID
|
||||
|
||||
showToastNotification({
|
||||
title: instanceName,
|
||||
message: `Session ${label ? `"${label}"` : sessionID} was compacted`,
|
||||
message: tGlobal("sessionEvents.sessionCompactedToast", { label: displayLabel }),
|
||||
variant: "info",
|
||||
duration: 10000,
|
||||
})
|
||||
@@ -428,7 +430,7 @@ function handleSessionError(_instanceId: string, event: EventSessionError): void
|
||||
const error = event.properties?.error
|
||||
log.error(`[SSE] Session error:`, error)
|
||||
|
||||
let message = "Unknown error"
|
||||
let message = tGlobal("sessionEvents.sessionError.unknown")
|
||||
|
||||
if (error) {
|
||||
if ("data" in error && error.data && typeof error.data === "object" && "message" in error.data) {
|
||||
@@ -438,8 +440,8 @@ function handleSessionError(_instanceId: string, event: EventSessionError): void
|
||||
}
|
||||
}
|
||||
|
||||
showAlertDialog(`Error: ${message}`, {
|
||||
title: "Session error",
|
||||
showAlertDialog(tGlobal("sessionEvents.sessionError.message", { message }), {
|
||||
title: tGlobal("sessionEvents.sessionError.title"),
|
||||
variant: "error",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { instances } from "./instances"
|
||||
import { showConfirmDialog } from "./alerts"
|
||||
import { getLogger } from "../lib/logger"
|
||||
import { requestData } from "../lib/opencode-api"
|
||||
import { tGlobal } from "../lib/i18n"
|
||||
|
||||
const log = getLogger("session")
|
||||
|
||||
@@ -650,12 +651,12 @@ async function cleanupBlankSessions(instanceId: string, excludeSessionId?: strin
|
||||
|
||||
if (fetchIfNeeded) {
|
||||
const confirmed = await showConfirmDialog(
|
||||
"This cleanup may be slow, and may delete sessions you didn't intend to delete. Are you sure?",
|
||||
tGlobal("sessionState.cleanup.deepConfirm.message"),
|
||||
{
|
||||
title: "Deep Clean Sessions",
|
||||
detail: "Deep Clean Sessions will delete all sessions that have no messages, remove any finished sub-agent sessions, and clear out any unused forks of a session.",
|
||||
confirmLabel: "Continue",
|
||||
cancelLabel: "Cancel"
|
||||
title: tGlobal("sessionState.cleanup.deepConfirm.title"),
|
||||
detail: tGlobal("sessionState.cleanup.deepConfirm.detail"),
|
||||
confirmLabel: tGlobal("sessionState.cleanup.deepConfirm.confirmLabel"),
|
||||
cancelLabel: tGlobal("sessionState.cleanup.deepConfirm.cancelLabel"),
|
||||
}
|
||||
)
|
||||
if (!confirmed) return
|
||||
@@ -680,7 +681,9 @@ async function cleanupBlankSessions(instanceId: string, excludeSessionId?: strin
|
||||
|
||||
if (deletedCount > 0) {
|
||||
showToastNotification({
|
||||
message: `Cleaned up ${deletedCount} blank session${deletedCount === 1 ? "" : "s"}`,
|
||||
message: deletedCount === 1
|
||||
? tGlobal("sessionState.cleanup.toast.one", { count: deletedCount })
|
||||
: tGlobal("sessionState.cleanup.toast.other", { count: deletedCount }),
|
||||
variant: "info"
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user