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
@@ -33,6 +33,7 @@ import type {
|
||||
GitHubPullRequestSummary,
|
||||
} from '@/lib/api/types';
|
||||
import type { ProjectRef } from '@/lib/worktrees/worktreeManager';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
type GitHubTab = 'issues' | 'prs';
|
||||
|
||||
@@ -56,6 +57,7 @@ export function GitHubIntegrationDialog({
|
||||
onOpenChange,
|
||||
onSelect,
|
||||
}: GitHubIntegrationDialogProps) {
|
||||
const { t } = useI18n();
|
||||
const isMobile = useUIStore((state) => state.isMobile);
|
||||
const { github } = useRuntimeAPIs();
|
||||
const githubAuthStatus = useGitHubAuthStore((state) => state.status);
|
||||
@@ -101,7 +103,7 @@ export function GitHubIntegrationDialog({
|
||||
if (activeTab === 'issues' && github.issuesList) {
|
||||
const result = await github.issuesList(projectDirectory, { page: 1 });
|
||||
if (result.connected === false) {
|
||||
setError('GitHub not connected');
|
||||
setError(t('session.githubIntegration.error.notConnected'));
|
||||
setIssues([]);
|
||||
} else {
|
||||
setIssues(result.issues ?? []);
|
||||
@@ -111,7 +113,7 @@ export function GitHubIntegrationDialog({
|
||||
} else if (activeTab === 'prs' && github.prsList) {
|
||||
const result = await github.prsList(projectDirectory, { page: 1 });
|
||||
if (result.connected === false) {
|
||||
setError('GitHub not connected');
|
||||
setError(t('session.githubIntegration.error.notConnected'));
|
||||
setPrs([]);
|
||||
} else {
|
||||
setPrs(result.prs ?? []);
|
||||
@@ -120,11 +122,11 @@ export function GitHubIntegrationDialog({
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to load data');
|
||||
setError(err instanceof Error ? err.message : t('session.githubIntegration.error.loadDataFailed'));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [projectDirectory, github, githubAuthChecked, githubAuthStatus, activeTab]);
|
||||
}, [projectDirectory, github, githubAuthChecked, githubAuthStatus, activeTab, t]);
|
||||
|
||||
// Load more data
|
||||
const loadMore = React.useCallback(async () => {
|
||||
@@ -199,15 +201,15 @@ export function GitHubIntegrationDialog({
|
||||
|
||||
setValidations(prev => new Map(prev).set(branchName, {
|
||||
isValid: !isBlocked,
|
||||
error: isBlocked ? 'Branch is already checked out in a worktree' : null,
|
||||
error: isBlocked ? t('session.githubIntegration.validation.branchAlreadyCheckedOut') : null,
|
||||
}));
|
||||
} catch {
|
||||
setValidations(prev => new Map(prev).set(branchName, {
|
||||
isValid: false,
|
||||
error: 'Validation failed',
|
||||
error: t('session.githubIntegration.validation.failed'),
|
||||
}));
|
||||
}
|
||||
}, [projectRef, validations]);
|
||||
}, [projectRef, validations, t]);
|
||||
|
||||
// Validate PR branches when loaded
|
||||
React.useEffect(() => {
|
||||
@@ -297,12 +299,12 @@ export function GitHubIntegrationDialog({
|
||||
<div className="flex-1 flex flex-col items-center justify-center p-8 gap-4">
|
||||
<RiGithubLine className="h-12 w-12 text-muted-foreground" />
|
||||
<div className="text-center">
|
||||
<p className="typography-ui-label text-foreground">Connect to GitHub</p>
|
||||
<p className="typography-ui-label text-foreground">{t('session.githubIntegration.connect.title')}</p>
|
||||
<p className="typography-small text-muted-foreground mt-1">
|
||||
Link issues or pull requests to auto-fill worktree details
|
||||
{t('session.githubIntegration.connect.description')}
|
||||
</p>
|
||||
</div>
|
||||
<Button onClick={openGitHubSettings} size="sm">Connect GitHub</Button>
|
||||
<Button onClick={openGitHubSettings} size="sm">{t('session.githubIntegration.connect.action')}</Button>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
@@ -312,7 +314,9 @@ export function GitHubIntegrationDialog({
|
||||
<Input
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
placeholder={activeTab === 'issues' ? "Search issues or enter #123..." : "Search PRs or enter #456..."}
|
||||
placeholder={activeTab === 'issues'
|
||||
? t('session.githubIntegration.search.issuesPlaceholder')
|
||||
: t('session.githubIntegration.search.prsPlaceholder')}
|
||||
className="h-8 pl-9"
|
||||
/>
|
||||
</div>
|
||||
@@ -360,7 +364,7 @@ export function GitHubIntegrationDialog({
|
||||
))
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-[300px] text-center typography-small text-muted-foreground">
|
||||
No issues found
|
||||
{t('session.githubIntegration.empty.noIssuesFound')}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -372,7 +376,7 @@ export function GitHubIntegrationDialog({
|
||||
onClick={() => void loadMore()}
|
||||
className="h-7 text-xs"
|
||||
>
|
||||
Load more
|
||||
{t('session.githubIntegration.actions.loadMore')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
@@ -427,7 +431,7 @@ export function GitHubIntegrationDialog({
|
||||
})
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-[300px] text-center typography-small text-muted-foreground">
|
||||
No pull requests found
|
||||
{t('session.githubIntegration.empty.noPullRequestsFound')}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -439,7 +443,7 @@ export function GitHubIntegrationDialog({
|
||||
onClick={() => void loadMore()}
|
||||
className="h-7 text-xs"
|
||||
>
|
||||
Load more
|
||||
{t('session.githubIntegration.actions.loadMore')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
@@ -473,7 +477,9 @@ export function GitHubIntegrationDialog({
|
||||
<div className="flex items-center gap-2 px-2 h-8 rounded-md bg-muted/50 border border-border/50">
|
||||
<RiCheckLine className="h-3.5 w-3.5 text-status-success shrink-0" />
|
||||
<span className="typography-small truncate max-w-[150px]">
|
||||
{selectedIssue ? `Issue #${selectedIssue.number}` : `PR #${selectedPr?.number}`}
|
||||
{selectedIssue
|
||||
? t('session.githubIntegration.selected.issueNumber', { number: selectedIssue.number })
|
||||
: t('session.githubIntegration.selected.prNumber', { number: selectedPr?.number ?? '' })}
|
||||
</span>
|
||||
<button
|
||||
onClick={handleClear}
|
||||
@@ -490,10 +496,10 @@ export function GitHubIntegrationDialog({
|
||||
<Checkbox
|
||||
checked={includeDiff}
|
||||
onChange={(checked) => setIncludeDiff(checked)}
|
||||
ariaLabel="Include PR diff in session context"
|
||||
ariaLabel={t('session.githubIntegration.includeDiffAria')}
|
||||
/>
|
||||
<span className="typography-small text-foreground">
|
||||
Include PR diff
|
||||
{t('session.githubIntegration.includeDiff')}
|
||||
</span>
|
||||
</label>
|
||||
)}
|
||||
@@ -510,7 +516,7 @@ export function GitHubIntegrationDialog({
|
||||
onClick={() => onOpenChange(false)}
|
||||
className={cn(isMobile && 'flex-1')}
|
||||
>
|
||||
Cancel
|
||||
{t('session.githubIntegration.actions.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
@@ -518,7 +524,7 @@ export function GitHubIntegrationDialog({
|
||||
disabled={!canConfirm}
|
||||
className={cn(isMobile && 'flex-1')}
|
||||
>
|
||||
Select
|
||||
{t('session.githubIntegration.actions.select')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -529,21 +535,21 @@ export function GitHubIntegrationDialog({
|
||||
{isMobile ? (
|
||||
<MobileOverlayPanel
|
||||
open={open}
|
||||
title="Select from GitHub"
|
||||
title={t('session.githubIntegration.title')}
|
||||
onClose={() => onOpenChange(false)}
|
||||
footer={!isGitHubConnected ? undefined : footerContent}
|
||||
renderHeader={(closeButton) => (
|
||||
<div className="flex flex-col gap-2 px-3 py-2 border-b border-border/40">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="typography-ui-label font-semibold text-foreground">Select from GitHub</h2>
|
||||
<h2 className="typography-ui-label font-semibold text-foreground">{t('session.githubIntegration.title')}</h2>
|
||||
{closeButton}
|
||||
</div>
|
||||
{/* Tabs - using SortableTabsStrip */}
|
||||
<div className="w-full">
|
||||
<SortableTabsStrip
|
||||
items={[
|
||||
{ id: 'issues', label: 'Issues', icon: <RiGitBranchLine className="h-3.5 w-3.5" /> },
|
||||
{ id: 'prs', label: 'Pull Requests', icon: <RiGitPullRequestLine className="h-3.5 w-3.5" /> },
|
||||
{ id: 'issues', label: t('session.githubIntegration.tabs.issues'), icon: <RiGitBranchLine className="h-3.5 w-3.5" /> },
|
||||
{ id: 'prs', label: t('session.githubIntegration.tabs.pullRequests'), icon: <RiGitPullRequestLine className="h-3.5 w-3.5" /> },
|
||||
]}
|
||||
activeId={activeTab}
|
||||
onSelect={(id) => {
|
||||
@@ -560,7 +566,9 @@ export function GitHubIntegrationDialog({
|
||||
<div className="flex items-center gap-2 px-2 py-1 rounded-md bg-muted/50 border border-border/50">
|
||||
<RiCheckLine className="h-3.5 w-3.5 text-status-success shrink-0" />
|
||||
<span className="typography-small truncate flex-1">
|
||||
{selectedIssue ? `Issue #${selectedIssue.number}` : `PR #${selectedPr?.number}`}
|
||||
{selectedIssue
|
||||
? t('session.githubIntegration.selected.issueNumber', { number: selectedIssue.number })
|
||||
: t('session.githubIntegration.selected.prNumber', { number: selectedPr?.number ?? '' })}
|
||||
</span>
|
||||
<button
|
||||
onClick={handleClear}
|
||||
@@ -582,15 +590,15 @@ export function GitHubIntegrationDialog({
|
||||
<div className="flex items-center gap-3">
|
||||
<DialogTitle className="flex items-center gap-2 shrink-0">
|
||||
<RiGithubLine className="h-5 w-5" />
|
||||
Select from GitHub
|
||||
{t('session.githubIntegration.title')}
|
||||
</DialogTitle>
|
||||
|
||||
{/* Tabs - using SortableTabsStrip */}
|
||||
<div className="w-[220px]">
|
||||
<SortableTabsStrip
|
||||
items={[
|
||||
{ id: 'issues', label: 'Issues', icon: <RiGitBranchLine className="h-3.5 w-3.5" /> },
|
||||
{ id: 'prs', label: 'Pull Requests', icon: <RiGitPullRequestLine className="h-3.5 w-3.5" /> },
|
||||
{ id: 'issues', label: t('session.githubIntegration.tabs.issues'), icon: <RiGitBranchLine className="h-3.5 w-3.5" /> },
|
||||
{ id: 'prs', label: t('session.githubIntegration.tabs.pullRequests'), icon: <RiGitPullRequestLine className="h-3.5 w-3.5" /> },
|
||||
]}
|
||||
activeId={activeTab}
|
||||
onSelect={(id) => {
|
||||
|
||||
Reference in New Issue
Block a user