Add i18n foundation and translations (#1027)

* feat: add i18n foundation

* feat: localize sessions sidebar

* Localize multirun/scheduled tasks and fix dialog dropdown interactions

* localize git sidebar surface and add zh-CN keys

* feat(ui): localize context panel, diff/plan views, and context sidebar content

* fix(config): resolve user config home via fs/home before embedded home

* localize header/chat UI and complete model/worktree panel strings

* localize worktree + github issue/pr dialog flows

* localize settings sections and split settings i18n dictionaries

* localize additional settings sections and sidebars

* localize more settings pages and dialogs

* fix settings select trigger localization

* localize tunnel settings ui surface

* localize additional settings sections

* localize keyboard shortcuts labels in settings

* localize terminal and utility dialogs surfaces

* feat(i18n): localize remaining UI strings

* Add Ukrainian locale

* Add Spanish locale

* Add Brazilian Portuguese locale

* Polish locale translations
This commit is contained in:
Bohdan Triapitsyn
2026-04-26 14:03:39 +03:00
committed by GitHub
parent 87db2ea210
commit 7d7285655d
198 changed files with 24173 additions and 4365 deletions
+21 -16
View File
@@ -22,6 +22,7 @@ import { useTodosPersistStore } from "@/stores/useTodosPersistStore";
import { WorkingPlaceholder } from "./message/parts/WorkingPlaceholder";
import { isVSCodeRuntime } from "@/lib/desktop";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { useI18n } from "@/lib/i18n";
const statusConfig: Record<TodoStatus, { textClassName: string }> = {
in_progress: {
@@ -50,17 +51,17 @@ const priorityIcon: Record<TodoPriority, React.ReactNode> = {
low: <RiArrowDownSLine className="h-3.5 w-3.5" aria-hidden="true" />,
};
const statusLabel: Record<TodoStatus, string> = {
in_progress: "In progress",
pending: "Pending",
completed: "Completed",
cancelled: "Cancelled",
const statusLabelKey: Record<TodoStatus, string> = {
in_progress: "chat.statusRow.todo.status.inProgress",
pending: "chat.statusRow.todo.status.pending",
completed: "chat.statusRow.todo.status.completed",
cancelled: "chat.statusRow.todo.status.cancelled",
};
const priorityLabel: Record<TodoPriority, string> = {
high: "High priority",
medium: "Medium priority",
low: "Low priority",
const priorityLabelKey: Record<TodoPriority, string> = {
high: "chat.statusRow.todo.priority.high",
medium: "chat.statusRow.todo.priority.medium",
low: "chat.statusRow.todo.priority.low",
};
interface TodoItemRowProps {
@@ -68,7 +69,10 @@ interface TodoItemRowProps {
}
const TodoItemRow: React.FC<TodoItemRowProps> = ({ todo }) => {
const { t } = useI18n();
const config = statusConfig[todo.status] || statusConfig.pending;
const statusKey = statusLabelKey[todo.status] ?? statusLabelKey.pending;
const priorityKey = priorityLabelKey[todo.priority] ?? priorityLabelKey.medium;
const statusIcon =
todo.status === "in_progress" ? (
@@ -86,7 +90,7 @@ const TodoItemRow: React.FC<TodoItemRowProps> = ({ todo }) => {
<span className="flex-shrink-0">{statusIcon}</span>
</TooltipTrigger>
<TooltipContent side="left" sideOffset={6}>
{statusLabel[todo.status] ?? statusLabel.pending}
{t(statusKey as never)}
</TooltipContent>
</Tooltip>
<span
@@ -109,7 +113,7 @@ const TodoItemRow: React.FC<TodoItemRowProps> = ({ todo }) => {
</span>
</TooltipTrigger>
<TooltipContent side="right" sideOffset={6}>
{priorityLabel[todo.priority] ?? priorityLabel.medium}
{t(priorityKey as never)}
</TooltipContent>
</Tooltip>
</div>
@@ -154,6 +158,7 @@ export const StatusRow: React.FC<StatusRowProps> = ({
agentName,
leftAccessory,
}) => {
const { t } = useI18n();
const [isExpanded, setIsExpanded] = React.useState(false);
const currentSessionId = useSessionUIStore((state) => state.currentSessionId);
const todosRecord = useDirectorySync((state) => state.todo);
@@ -235,7 +240,7 @@ export const StatusRow: React.FC<StatusRowProps> = ({
type="button"
onClick={onAbort}
className="flex items-center justify-center h-[1.2rem] w-[1.2rem] text-[var(--status-error)] transition-opacity hover:opacity-80 focus-visible:outline-none flex-shrink-0"
aria-label="Stop generating"
aria-label={t('chat.statusRow.actions.stopGeneratingAria')}
>
<RiCloseCircleLine size={18} aria-hidden="true" />
</button>
@@ -254,10 +259,10 @@ export const StatusRow: React.FC<StatusRowProps> = ({
{activeTodo.content}
</span>
) : (
<span className="typography-ui-label">Tasks</span>
<span className="typography-ui-label">{t('chat.statusRow.tasksTitle')}</span>
)}
<span className="typography-meta">
{statusSummary.active} active · {statusSummary.left} left
{t('chat.statusRow.summary.activeLeft', { active: statusSummary.active, left: statusSummary.left })}
</span>
{isExpanded ? (
<RiArrowUpSLine className="h-3.5 w-3.5" />
@@ -281,7 +286,7 @@ export const StatusRow: React.FC<StatusRowProps> = ({
<div className="flex h-full items-center text-[var(--status-error)] pl-0.5">
<span className="flex items-center gap-1.5 typography-ui-label">
<RiCloseCircleLine size={16} aria-hidden="true" />
Aborted
{t('chat.statusRow.aborted')}
</span>
</div>
) : showAssistantStatus && shouldRenderPlaceholder ? (
@@ -323,7 +328,7 @@ export const StatusRow: React.FC<StatusRowProps> = ({
>
{/* Header */}
<div className="flex items-center gap-1.5 px-2 py-1 typography-ui-label font-medium text-muted-foreground">
<span>Tasks</span>
<span>{t('chat.statusRow.tasksTitle')}</span>
<span className="typography-meta tabular-nums">
{progress.completed}/{progress.total}
</span>