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
@@ -26,6 +26,7 @@ import {
setDirectoryShowHidden,
useDirectoryShowHidden,
} from '@/lib/directoryShowHidden';
import { useI18n } from '@/lib/i18n';
interface DirectoryExplorerDialogProps {
open: boolean;
@@ -36,6 +37,7 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
open,
onOpenChange,
}) => {
const { t } = useI18n();
const { currentDirectory, homeDirectory, isHomeReady } = useDirectoryStore();
const { addProject, getActiveProject } = useProjectsStore();
const [pendingPath, setPendingPath] = React.useState<string | null>(null);
@@ -97,8 +99,8 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
if (isDesktop) {
const accessResult = await requestAccess(targetPath);
if (!accessResult.success) {
toast.error('Unable to access directory', {
description: accessResult.error || 'Desktop denied directory access.',
toast.error(t('directoryExplorerDialog.toast.unableToAccessDirectory'), {
description: accessResult.error || t('directoryExplorerDialog.toast.desktopDeniedAccess'),
});
return;
}
@@ -107,8 +109,8 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
const startResult = await startAccessing(resolvedPath);
if (!startResult.success) {
toast.error('Failed to open directory', {
description: startResult.error || 'Desktop could not grant file access.',
toast.error(t('directoryExplorerDialog.toast.failedToOpenDirectory'), {
description: startResult.error || t('directoryExplorerDialog.toast.desktopCouldNotGrantAccess'),
});
return;
}
@@ -116,16 +118,16 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
const added = addProject(resolvedPath, { id: projectId });
if (!added) {
toast.error('Failed to add project', {
description: 'Please select a valid directory path.',
toast.error(t('directoryExplorerDialog.toast.failedToAddProject'), {
description: t('directoryExplorerDialog.toast.selectValidDirectoryPath'),
});
return;
}
handleClose();
} catch (error) {
toast.error('Failed to select directory', {
description: error instanceof Error ? error.message : 'Unknown error occurred.',
toast.error(t('directoryExplorerDialog.toast.failedToSelectDirectory'), {
description: error instanceof Error ? error.message : t('directoryExplorerDialog.toast.unknownError'),
});
} finally {
setIsConfirming(false);
@@ -137,6 +139,7 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
requestAccess,
startAccessing,
isConfirming,
t,
]);
const handleConfirm = React.useCallback(async () => {
@@ -215,16 +218,16 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
) : (
<RiCheckboxBlankLine className="h-4 w-4" />
)}
Show hidden
{t('directoryExplorerDialog.toggle.showHidden')}
</button>
);
const dialogHeader = (
<DialogHeader className="flex-shrink-0 px-4 pb-2 pt-[calc(var(--oc-safe-area-top,0px)+0.5rem)] sm:px-0 sm:pb-3 sm:pt-0">
<DialogTitle>Add project directory</DialogTitle>
<DialogTitle>{t('directoryExplorerDialog.title')}</DialogTitle>
<div className="hidden sm:flex sm:items-center sm:justify-between sm:gap-4">
<DialogDescription className="flex-1">
Choose a folder to add as a project.
{t('directoryExplorerDialog.description')}
</DialogDescription>
{showHiddenToggle}
</div>
@@ -237,7 +240,7 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
value={pathInputValue}
onChange={handlePathInputChange}
onKeyDown={handlePathInputKeyDown}
placeholder="Enter path or select from tree..."
placeholder={t('directoryExplorerDialog.pathInput.placeholder')}
className="font-mono typography-meta"
spellCheck={false}
autoComplete="off"
@@ -311,14 +314,14 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
disabled={isConfirming}
className="flex-1 sm:flex-none sm:w-auto"
>
Cancel
{t('directoryExplorerDialog.actions.cancel')}
</Button>
<Button
onClick={handleConfirm}
disabled={isConfirming || !hasUserSelection || (!pendingPath && !pathInputValue.trim())}
className="flex-1 sm:flex-none sm:w-auto sm:min-w-[140px]"
>
{isConfirming ? 'Adding...' : 'Add Project'}
{isConfirming ? t('directoryExplorerDialog.actions.adding') : t('directoryExplorerDialog.actions.addProject')}
</Button>
</>
);
@@ -328,7 +331,7 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
<MobileOverlayPanel
open={open}
onClose={() => onOpenChange(false)}
title="Add project directory"
title={t('directoryExplorerDialog.title')}
className="max-w-full"
contentMaxHeightClassName="max-h-[min(70vh,520px)] h-[min(70vh,520px)]"
footer={<div className="flex flex-row gap-2">{renderActionButtons()}</div>}