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:
committed by
GitHub
parent
87db2ea210
commit
7d7285655d
@@ -26,6 +26,7 @@ import type { ToolPopupContent, DiffViewMode } from './types';
|
||||
import { DiffViewToggle } from './DiffViewToggle';
|
||||
import { VirtualizedCodeBlock, type CodeLine } from './parts/VirtualizedCodeBlock';
|
||||
import { JsonTreeView } from '@/components/ui/JsonTreeView';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
interface ToolOutputDialogProps {
|
||||
popup: ToolPopupContent;
|
||||
@@ -302,6 +303,7 @@ const ImagePreviewDialog: React.FC<{
|
||||
onOpenChange: (open: boolean) => void;
|
||||
isMobile: boolean;
|
||||
}> = ({ popup, onOpenChange, isMobile }) => {
|
||||
const { t } = useI18n();
|
||||
const gallery = React.useMemo(() => {
|
||||
const baseImage = popup.image;
|
||||
if (!baseImage) return [] as Array<{ url: string; mimeType?: string; filename?: string; size?: number }>;
|
||||
@@ -434,7 +436,7 @@ const ImagePreviewDialog: React.FC<{
|
||||
onMouseDown={(event) => event.stopPropagation()}
|
||||
onClick={showPrevious}
|
||||
className="absolute left-3 top-1/2 -translate-y-1/2 z-10 h-10 w-10 flex items-center justify-center rounded-full bg-black/40 text-foreground/90 hover:bg-black/55 focus:outline-none focus:ring-2 focus:ring-primary/60"
|
||||
aria-label="Previous image"
|
||||
aria-label={t('chat.toolOutputDialog.image.previousAria')}
|
||||
>
|
||||
<RiArrowLeftSLine className="h-6 w-6" />
|
||||
</button>
|
||||
@@ -443,7 +445,7 @@ const ImagePreviewDialog: React.FC<{
|
||||
onMouseDown={(event) => event.stopPropagation()}
|
||||
onClick={showNext}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 z-10 h-10 w-10 flex items-center justify-center rounded-full bg-black/40 text-foreground/90 hover:bg-black/55 focus:outline-none focus:ring-2 focus:ring-primary/60"
|
||||
aria-label="Next image"
|
||||
aria-label={t('chat.toolOutputDialog.image.nextAria')}
|
||||
>
|
||||
<RiArrowRightSLine className="h-6 w-6" />
|
||||
</button>
|
||||
@@ -472,7 +474,7 @@ const ImagePreviewDialog: React.FC<{
|
||||
type="button"
|
||||
className="h-8 w-8 flex items-center justify-center rounded-lg text-muted-foreground/80 hover:text-foreground focus:outline-none focus:ring-2 focus:ring-primary/60"
|
||||
onClick={() => onOpenChange(false)}
|
||||
aria-label="Close image preview"
|
||||
aria-label={t('chat.toolOutputDialog.image.closeAria')}
|
||||
>
|
||||
<RiCloseLine className="h-4 w-4" />
|
||||
</button>
|
||||
@@ -632,6 +634,7 @@ const MermaidPreviewDialog: React.FC<{
|
||||
onOpenChange: (open: boolean) => void;
|
||||
isMobile: boolean;
|
||||
}> = ({ popup, onOpenChange, isMobile }) => {
|
||||
const { t } = useI18n();
|
||||
const [source, setSource] = React.useState<string>(popup.mermaid?.source || '');
|
||||
const [status, setStatus] = React.useState<'idle' | 'loading' | 'ready' | 'error'>(popup.mermaid?.source ? 'ready' : 'idle');
|
||||
const [errorMessage, setErrorMessage] = React.useState<string>('');
|
||||
@@ -707,7 +710,7 @@ const MermaidPreviewDialog: React.FC<{
|
||||
const target = popup.mermaid;
|
||||
if (!target?.url) {
|
||||
setStatus('error');
|
||||
setErrorMessage('Missing Mermaid source URL.');
|
||||
setErrorMessage(t('chat.toolOutputDialog.mermaid.missingSource'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -773,7 +776,7 @@ const MermaidPreviewDialog: React.FC<{
|
||||
return;
|
||||
}
|
||||
setStatus('error');
|
||||
setErrorMessage(error instanceof Error ? error.message : 'Unable to load Mermaid diagram.');
|
||||
setErrorMessage(error instanceof Error ? error.message : t('chat.toolOutputDialog.mermaid.loadFailed'));
|
||||
});
|
||||
}, [decodeDataUrl, normalizeFilePath, popup.mermaid]);
|
||||
|
||||
@@ -918,7 +921,7 @@ const MermaidPreviewDialog: React.FC<{
|
||||
type="button"
|
||||
className="h-8 w-8 flex items-center justify-center rounded-lg text-muted-foreground/80 hover:text-foreground focus:outline-none focus:ring-2 focus:ring-primary/60"
|
||||
onClick={() => onOpenChange(false)}
|
||||
aria-label="Close diagram preview"
|
||||
aria-label={t('chat.toolOutputDialog.mermaid.closeAria')}
|
||||
>
|
||||
<RiCloseLine className="h-4 w-4" />
|
||||
</button>
|
||||
@@ -931,14 +934,14 @@ const MermaidPreviewDialog: React.FC<{
|
||||
{status === 'loading' && (
|
||||
<div className="h-full min-h-28 flex items-center justify-center gap-2 text-muted-foreground typography-meta">
|
||||
<RiLoader4Line className="h-4 w-4 animate-spin" />
|
||||
<span>Loading diagram...</span>
|
||||
<span>{t('chat.toolOutputDialog.mermaid.loading')}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{status === 'error' && (
|
||||
<div className="rounded-xl border border-border/30 bg-muted/20 p-3 space-y-3">
|
||||
<p className="typography-markdown" style={{ color: 'var(--status-error)' }}>
|
||||
{errorMessage || 'Unable to render Mermaid diagram.'}
|
||||
{errorMessage || t('chat.toolOutputDialog.mermaid.renderFailed')}
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
@@ -951,7 +954,7 @@ const MermaidPreviewDialog: React.FC<{
|
||||
color: 'var(--surface-foreground)',
|
||||
}}
|
||||
>
|
||||
Retry
|
||||
{t('chat.toolOutputDialog.mermaid.retry')}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
@@ -983,6 +986,7 @@ const MermaidPreviewDialog: React.FC<{
|
||||
};
|
||||
|
||||
const ToolOutputDialog: React.FC<ToolOutputDialogProps> = ({ popup, onOpenChange, syntaxTheme, isMobile }) => {
|
||||
const { t } = useI18n();
|
||||
const [diffViewMode, setDiffViewMode] = React.useState<DiffViewMode>('unified');
|
||||
const pierreThemeConfig = usePierreThemeConfig();
|
||||
|
||||
@@ -1112,7 +1116,13 @@ const ToolOutputDialog: React.FC<ToolOutputDialogProps> = ({ popup, onOpenChange
|
||||
|
||||
if (tool === 'todowrite' || tool === 'todoread') {
|
||||
return (
|
||||
renderTodoOutput(popup.content) || (
|
||||
renderTodoOutput(popup.content, {
|
||||
total: t('chat.todo.total'),
|
||||
inProgress: t('chat.todo.inProgress'),
|
||||
pending: t('chat.todo.pending'),
|
||||
completed: t('chat.todo.completed'),
|
||||
cancelled: t('chat.todo.cancelled'),
|
||||
}) || (
|
||||
<SyntaxHighlighter
|
||||
style={syntaxTheme}
|
||||
language="json"
|
||||
@@ -1214,8 +1224,8 @@ const ToolOutputDialog: React.FC<ToolOutputDialogProps> = ({ popup, onOpenChange
|
||||
</div>
|
||||
) : (
|
||||
<div className="p-8 text-muted-foreground typography-ui-header">
|
||||
<div className="mb-2">Command completed successfully</div>
|
||||
<div className="typography-meta">No output was produced</div>
|
||||
<div className="mb-2">{t('chat.toolOutputDialog.commandCompleted')}</div>
|
||||
<div className="typography-meta">{t('chat.toolOutputDialog.noOutputProduced')}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user