feat(desktop): add i18n infrastructure with react-intl (#8105)
This commit is contained in:
@@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { Outlet, useLocation } from 'react-router-dom';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Menu } from 'lucide-react';
|
||||
import { defineMessages, useIntl } from '../../i18n';
|
||||
import { Button } from '../ui/button';
|
||||
import ChatSessionsContainer from '../ChatSessionsContainer';
|
||||
import { useChatContext } from '../../contexts/ChatContext';
|
||||
@@ -11,6 +12,17 @@ import { NAV_DIMENSIONS, Z_INDEX } from './constants';
|
||||
import { cn } from '../../utils';
|
||||
import { UserInput } from '../../types/message';
|
||||
|
||||
const i18n = defineMessages({
|
||||
closeNavigation: {
|
||||
id: 'appLayout.closeNavigation',
|
||||
defaultMessage: 'Close navigation',
|
||||
},
|
||||
openNavigation: {
|
||||
id: 'appLayout.openNavigation',
|
||||
defaultMessage: 'Open navigation',
|
||||
},
|
||||
});
|
||||
|
||||
interface AppLayoutContentProps {
|
||||
activeSessions: Array<{
|
||||
sessionId: string;
|
||||
@@ -19,6 +31,7 @@ interface AppLayoutContentProps {
|
||||
}
|
||||
|
||||
const AppLayoutContent: React.FC<AppLayoutContentProps> = ({ activeSessions }) => {
|
||||
const intl = useIntl();
|
||||
const location = useLocation();
|
||||
const safeIsMacOS = (window?.electron?.platform || 'darwin') === 'darwin';
|
||||
const chatContext = useChatContext();
|
||||
@@ -199,7 +212,7 @@ const AppLayoutContent: React.FC<AppLayoutContentProps> = ({ activeSessions }) =
|
||||
className="no-drag hover:!bg-background-tertiary"
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
title={isNavExpanded ? 'Close navigation' : 'Open navigation'}
|
||||
title={isNavExpanded ? intl.formatMessage(i18n.closeNavigation) : intl.formatMessage(i18n.openNavigation)}
|
||||
>
|
||||
<Menu className="w-5 h-5" />
|
||||
</Button>
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
import React, { useState } from 'react';
|
||||
import { GripVertical, ChevronDown, ChevronRight, Plus } from 'lucide-react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { defineMessages, useIntl } from '../../i18n';
|
||||
import { cn } from '../../utils';
|
||||
import { DropdownMenu, DropdownMenuTrigger } from '../ui/dropdown-menu';
|
||||
import { ChatSessionsDropdown, SessionsList } from './navigation';
|
||||
import type { NavigationRendererProps } from './navigation/types';
|
||||
|
||||
const i18n = defineMessages({
|
||||
newChat: {
|
||||
id: 'condensedRenderer.newChat',
|
||||
defaultMessage: 'New Chat',
|
||||
},
|
||||
});
|
||||
|
||||
export const CondensedRenderer: React.FC<NavigationRendererProps> = ({
|
||||
isOverlayMode,
|
||||
navigationPosition,
|
||||
@@ -26,6 +34,7 @@ export const CondensedRenderer: React.FC<NavigationRendererProps> = ({
|
||||
drag,
|
||||
navFocusRef,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const [chatPopoverOpen, setChatPopoverOpen] = useState(false);
|
||||
|
||||
const isVertical = navigationPosition === 'left' || navigationPosition === 'right';
|
||||
@@ -176,7 +185,7 @@ export const CondensedRenderer: React.FC<NavigationRendererProps> = ({
|
||||
'bg-background-tertiary hover:bg-background-inverse hover:text-text-inverse',
|
||||
'flex items-center justify-center'
|
||||
)}
|
||||
title="New Chat"
|
||||
title={intl.formatMessage(i18n.newChat)}
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
</motion.button>
|
||||
|
||||
@@ -8,9 +8,21 @@ import {
|
||||
import { SessionIndicators } from '../../SessionIndicators';
|
||||
import { cn } from '../../../utils';
|
||||
import { getSessionDisplayName, truncateMessage } from '../../../hooks/useNavigationSessions';
|
||||
import { defineMessages, useIntl } from '../../../i18n';
|
||||
import type { Session } from '../../../api';
|
||||
import type { SessionStatus } from './types';
|
||||
|
||||
const i18n = defineMessages({
|
||||
newChat: {
|
||||
id: 'chatSessionsDropdown.newChat',
|
||||
defaultMessage: 'New Chat',
|
||||
},
|
||||
showAll: {
|
||||
id: 'chatSessionsDropdown.showAll',
|
||||
defaultMessage: 'Show All',
|
||||
},
|
||||
});
|
||||
|
||||
interface ChatSessionsDropdownProps {
|
||||
sessions: Session[];
|
||||
activeSessionId?: string;
|
||||
@@ -34,6 +46,7 @@ export const ChatSessionsDropdown: React.FC<ChatSessionsDropdownProps> = ({
|
||||
onSessionClick,
|
||||
onShowAll,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<DropdownMenuContent
|
||||
className="w-64 p-1 bg-background-primary border-border-secondary rounded-lg shadow-lg"
|
||||
@@ -47,7 +60,7 @@ export const ChatSessionsDropdown: React.FC<ChatSessionsDropdownProps> = ({
|
||||
className="flex items-center gap-2 px-3 py-2 text-sm rounded-lg cursor-pointer"
|
||||
>
|
||||
<Plus className="w-4 h-4 flex-shrink-0" />
|
||||
<span>New Chat</span>
|
||||
<span>{intl.formatMessage(i18n.newChat)}</span>
|
||||
</DropdownMenuItem>
|
||||
|
||||
{sessions.length > 0 && <DropdownMenuSeparator className="my-1" />}
|
||||
@@ -96,7 +109,7 @@ export const ChatSessionsDropdown: React.FC<ChatSessionsDropdownProps> = ({
|
||||
className="flex items-center gap-2 px-3 py-2 text-sm rounded-lg cursor-pointer text-text-secondary"
|
||||
>
|
||||
<History className="w-4 h-4 flex-shrink-0" />
|
||||
<span>Show All</span>
|
||||
<span>{intl.formatMessage(i18n.showAll)}</span>
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -8,6 +8,22 @@ import { getSessionDisplayName } from '../../../hooks/useNavigationSessions';
|
||||
import { updateSessionName } from '../../../api';
|
||||
import type { Session } from '../../../api';
|
||||
import type { SessionStatus } from './types';
|
||||
import { defineMessages, useIntl } from '../../../i18n';
|
||||
|
||||
const i18n = defineMessages({
|
||||
startNewChat: {
|
||||
id: 'sessionsList.startNewChat',
|
||||
defaultMessage: 'Start New Chat',
|
||||
},
|
||||
untitledSession: {
|
||||
id: 'sessionsList.untitledSession',
|
||||
defaultMessage: 'Untitled session',
|
||||
},
|
||||
showAll: {
|
||||
id: 'sessionsList.showAll',
|
||||
defaultMessage: 'Show All',
|
||||
},
|
||||
});
|
||||
|
||||
interface SessionsListProps {
|
||||
sessions: Session[];
|
||||
@@ -32,6 +48,7 @@ export const SessionsList: React.FC<SessionsListProps> = ({
|
||||
onNewChat,
|
||||
onShowAll,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const [editingSessionId, setEditingSessionId] = useState<string | null>(null);
|
||||
|
||||
const handleSaveSessionName = useCallback(
|
||||
@@ -68,7 +85,7 @@ export const SessionsList: React.FC<SessionsListProps> = ({
|
||||
>
|
||||
<div className="w-4 flex-shrink-0" />
|
||||
<Plus className="w-4 h-4 flex-shrink-0 text-text-secondary" />
|
||||
<span className="text-text-primary">Start New Chat</span>
|
||||
<span className="text-text-primary">{intl.formatMessage(i18n.startNewChat)}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -105,7 +122,7 @@ export const SessionsList: React.FC<SessionsListProps> = ({
|
||||
<InlineEditText
|
||||
value={getSessionDisplayName(session)}
|
||||
onSave={(newName) => handleSaveSessionName(session.id, newName)}
|
||||
placeholder="Untitled session"
|
||||
placeholder={intl.formatMessage(i18n.untitledSession)}
|
||||
disabled={isStreaming}
|
||||
singleClickEdit={false}
|
||||
className="truncate text-text-primary flex-1 !px-0 !py-0 hover:bg-transparent"
|
||||
@@ -134,7 +151,7 @@ export const SessionsList: React.FC<SessionsListProps> = ({
|
||||
>
|
||||
<div className="w-4 flex-shrink-0" />
|
||||
<History className="w-4 h-4 flex-shrink-0" />
|
||||
<span>Show All</span>
|
||||
<span>{intl.formatMessage(i18n.showAll)}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user