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:
Shantur Rathore
2026-01-26 12:26:12 +00:00
parent 33939f4096
commit 5b1e21345f
88 changed files with 2080 additions and 822 deletions

View File

@@ -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,

View File

@@ -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",
})
}

View File

@@ -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"
})
}