feat(i18n): add multi-language support (ja/ko/vi/ru/zh-TW) with system auto-detection
- Extend AppLanguage to 7 locales: zh-CN, zh-TW, en, ja, ko, vi, ru - Auto-detect system language via navigator.languages; fallback to English - Add BCP47 locale mapping for date formatting and sorting - Create 50 translation JSON files (5 languages × 10 namespaces, 1137 keys each) - Update language selector with native language names - Extend locale key parity test to cover all 7 languages (70 assertions) - Add VNC/RDP desktop, serial terminal, AI agent, port forwarding, and SFTP features - Add credential encryption, host metrics, session backup crypto, and zmodem bridge - Update Rust backend: desktop services, host metrics, SSH/SFTP enhancements
This commit is contained in:
1 parent
fea6d8db04
commit
b0d0e3a5f3
301 files changed
+28002
-2705
No files matched your search
+24
-13
@@ -8,7 +8,8 @@
|
||||
*/
|
||||
|
||||
import type { ReactNode } from 'react'
|
||||
import { PanelLeft } from 'lucide-react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { ChevronsRight } from 'lucide-react'
|
||||
import Sidebar from '../components/layout/Sidebar'
|
||||
import TabBar from '../components/layout/TabBar'
|
||||
import TabContent from '../components/layout/TabContent'
|
||||
@@ -24,8 +25,10 @@ export interface AppShellProps {
|
||||
onToggleSidebar: () => void
|
||||
onToggleMobileSidebar: () => void
|
||||
onCloseMobileSidebar: () => void
|
||||
onNewSession: () => void
|
||||
onNewSession: (group?: string) => void
|
||||
onQuickConnect: () => void
|
||||
onRdpConnect?: () => void
|
||||
onVncConnect?: () => void
|
||||
onOpenLauncher: () => void
|
||||
onEditSession: (session: Session) => void
|
||||
children?: ReactNode
|
||||
@@ -42,21 +45,20 @@ function TitleBar() {
|
||||
className="bg-dark-900/95 flex h-10 flex-shrink-0 select-none items-center border-b border-dark-800"
|
||||
>
|
||||
<div data-tauri-drag-region className="h-full w-[78px] flex-shrink-0" aria-hidden />
|
||||
<div data-tauri-drag-region className="flex h-full min-w-0 flex-1 items-center justify-center px-2">
|
||||
<span className="pointer-events-none truncate text-[11px] tracking-wide text-txt-faint">OpsXShell</span>
|
||||
</div>
|
||||
<div data-tauri-drag-region className="flex h-full min-w-0 flex-1 items-center justify-center px-2" />
|
||||
<div data-tauri-drag-region className="h-full w-[78px] flex-shrink-0" aria-hidden />
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
function MobileMenuButton({ onClick }: { onClick: () => void }) {
|
||||
const { t } = useTranslation('common')
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
className="titlebar-no-drag flex h-10 w-10 items-center justify-center text-txt-muted transition-colors hover:bg-dark-800 hover:text-content-primary"
|
||||
aria-label="切换侧边栏"
|
||||
aria-label={t('sidebar.toggle')}
|
||||
>
|
||||
<svg className="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
|
||||
@@ -66,15 +68,16 @@ function MobileMenuButton({ onClick }: { onClick: () => void }) {
|
||||
}
|
||||
|
||||
function ExpandSidebarButton({ onClick }: { onClick: () => void }) {
|
||||
const { t } = useTranslation('common')
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
className="titlebar-no-drag flex h-10 w-10 flex-shrink-0 items-center justify-center border-r border-dark-700/40 text-txt-muted transition-colors hover:bg-dark-700/60 hover:text-content-primary"
|
||||
title="展开侧栏 (Ctrl/Cmd+B)"
|
||||
aria-label="展开侧栏"
|
||||
className="titlebar-no-drag group flex h-10 w-9 flex-shrink-0 items-center justify-center border-r border-dark-700/30 text-txt-faint transition-colors hover:bg-dark-700/50 hover:text-txt-muted"
|
||||
title={t('sidebar.expand')}
|
||||
aria-label={t('sidebar.expandAria')}
|
||||
>
|
||||
<PanelLeft className="h-4 w-4" />
|
||||
<ChevronsRight className="h-3.5 w-3.5 opacity-70 transition-opacity group-hover:opacity-100" strokeWidth={1.75} />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
@@ -89,12 +92,14 @@ export default function AppShell({
|
||||
onCloseMobileSidebar,
|
||||
onNewSession,
|
||||
onQuickConnect,
|
||||
onRdpConnect,
|
||||
onVncConnect,
|
||||
onOpenLauncher,
|
||||
onEditSession,
|
||||
children,
|
||||
}: AppShellProps) {
|
||||
const handleSidebarNewSession = () => {
|
||||
onNewSession()
|
||||
const handleSidebarNewSession = (group?: string) => {
|
||||
onNewSession(group)
|
||||
if (isMobile) onCloseMobileSidebar()
|
||||
}
|
||||
|
||||
@@ -151,7 +156,13 @@ export default function AppShell({
|
||||
</div>
|
||||
|
||||
<div className="titlebar-no-drag min-h-0 flex-1 overflow-hidden">
|
||||
<TabContent activeTabId={activeTabId} onNewSession={onNewSession} onQuickConnect={onQuickConnect} />
|
||||
<TabContent
|
||||
activeTabId={activeTabId}
|
||||
onNewSession={onNewSession}
|
||||
onQuickConnect={onQuickConnect}
|
||||
onRdpConnect={onRdpConnect}
|
||||
onVncConnect={onVncConnect}
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user