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
@@ -17,6 +17,7 @@ import { ChangeRow } from './ChangeRow';
import type { GitStatus } from '@/lib/api/types';
import { cn } from '@/lib/utils';
import { useUIStore } from '@/stores/useUIStore';
import { useI18n } from '@/lib/i18n';
interface ChangesSectionProps {
changeEntries: GitStatus['files'];
@@ -183,6 +184,7 @@ export const ChangesSection: React.FC<ChangesSectionProps> = ({
maxListHeightClassName,
onVisiblePathsChange,
}) => {
const { t } = useI18n();
const scrollRef = React.useRef<HTMLDivElement | null>(null);
const gitChangesViewMode = useUIStore((state) => state.gitChangesViewMode);
const isTreeView = gitChangesViewMode === 'tree';
@@ -386,7 +388,9 @@ export const ChangesSection: React.FC<ChangesSectionProps> = ({
type="button"
onClick={() => toggleDirectoryExpanded(directory.path)}
className="flex size-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"
aria-label={isExpanded ? `Collapse ${directory.path}` : `Expand ${directory.path}`}
aria-label={isExpanded
? t('gitView.changes.collapseDirectoryAria', { path: directory.path })
: t('gitView.changes.expandDirectoryAria', { path: directory.path })}
>
{isExpanded ? <RiArrowDownSLine className="size-4" /> : <RiArrowRightSLine className="size-4" />}
</button>
@@ -395,7 +399,7 @@ export const ChangesSection: React.FC<ChangesSectionProps> = ({
type="button"
role="checkbox"
aria-checked={selectionState === 'partial' ? 'mixed' : selectionState === 'all'}
aria-label={`Toggle selection for directory ${directory.path}`}
aria-label={t('gitView.changes.toggleDirectorySelectionAria', { path: directory.path })}
onClick={() => toggleDirectorySelection(directory)}
className="flex size-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"
>
@@ -443,7 +447,7 @@ export const ChangesSection: React.FC<ChangesSectionProps> = ({
<section className={containerClassName}>
<header className={headerClassName}>
<div className="flex min-w-0 items-center gap-2">
<h3 className="typography-ui-header font-semibold text-foreground">Changes</h3>
<h3 className="typography-ui-header font-semibold text-foreground">{t('gitView.changes.title')}</h3>
{totalCount > 0 ? (
<div
className={cn(
@@ -457,7 +461,7 @@ export const ChangesSection: React.FC<ChangesSectionProps> = ({
indeterminate={isPartiallySelected}
disabled={isRevertingAll}
onChange={() => (areAllSelected ? onClearSelection() : onSelectAll())}
ariaLabel={areAllSelected ? 'Clear file selection' : 'Select all files'}
ariaLabel={areAllSelected ? t('gitView.changes.clearSelectionAria') : t('gitView.changes.selectAllAria')}
/>
<span className="typography-meta text-muted-foreground">{selectedCount}/{totalCount}</span>
</div>
@@ -471,7 +475,7 @@ export const ChangesSection: React.FC<ChangesSectionProps> = ({
onClick={() => setConfirmRevertAllOpen(true)}
disabled={isRevertingAll}
>
Revert all
{t('gitView.changes.revertAll')}
</Button>
) : null}
</div>
@@ -513,7 +517,7 @@ export const ChangesSection: React.FC<ChangesSectionProps> = ({
})}
</div>
) : (
<div role="list" aria-label="Changed files">
<div role="list" aria-label={t('gitView.changes.changedFilesAria')}>
{rowItems.map((item, index) => (
<div
key={isTreeView ? (item as FlattenedTreeRow).key : `file:${(item as GitStatus['files'][number]).path}`}
@@ -535,17 +539,19 @@ export const ChangesSection: React.FC<ChangesSectionProps> = ({
<Dialog open={confirmRevertAllOpen} onOpenChange={(open) => { if (!isRevertingAll) setConfirmRevertAllOpen(open); }}>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle>Revert all changes?</DialogTitle>
<DialogTitle>{t('gitView.changes.revertAllDialogTitle')}</DialogTitle>
<DialogDescription>
This will discard local changes for {totalCount} file{totalCount === 1 ? '' : 's'} in the list.
{totalCount === 1
? t('gitView.changes.revertAllDescriptionSingle', { count: totalCount })
: t('gitView.changes.revertAllDescriptionPlural', { count: totalCount })}
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button variant="outline" size="sm" onClick={() => setConfirmRevertAllOpen(false)} disabled={isRevertingAll}>
Cancel
{t('gitView.common.cancel')}
</Button>
<Button variant="destructive" size="sm" onClick={() => void handleConfirmRevertAll()} disabled={isRevertingAll}>
{isRevertingAll ? 'Reverting...' : 'Revert all'}
{isRevertingAll ? t('gitView.changes.reverting') : t('gitView.changes.revertAll')}
</Button>
</DialogFooter>
</DialogContent>